fix(android): unblock mic permission startup
This commit is contained in:
@@ -23,8 +23,6 @@
|
||||
// test is actually exercised. The non-Android short-circuit (channel
|
||||
// == null) is covered explicitly by the final test.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
@@ -50,13 +48,10 @@ void main() {
|
||||
}) async {
|
||||
const codec = StandardMethodCodec();
|
||||
final encoded = codec.encodeMethodCall(
|
||||
MethodCall(
|
||||
methodPermissionStateChanged,
|
||||
<String, dynamic>{
|
||||
'permission': permission,
|
||||
'state': state,
|
||||
},
|
||||
),
|
||||
MethodCall(methodPermissionStateChanged, <String, dynamic>{
|
||||
'permission': permission,
|
||||
'state': state,
|
||||
}),
|
||||
);
|
||||
await TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.handlePlatformMessage(channel.name, encoded, (_) {});
|
||||
@@ -73,11 +68,11 @@ void main() {
|
||||
// response set `outgoingResponder`.
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, (call) async {
|
||||
outgoingCalls.add(call);
|
||||
final r = outgoingResponder;
|
||||
if (r != null) return r(call);
|
||||
return null;
|
||||
});
|
||||
outgoingCalls.add(call);
|
||||
final r = outgoingResponder;
|
||||
if (r != null) return r(call);
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
@@ -85,8 +80,7 @@ void main() {
|
||||
.setMockMethodCallHandler(channel, null);
|
||||
});
|
||||
|
||||
test(
|
||||
'SWE4-UV-041 / SDD-106: a fresh service exposes a deterministic '
|
||||
test('SWE4-UV-041 / SDD-106: a fresh service exposes a deterministic '
|
||||
'initial recordAudioState (granted on non-Android host, where these '
|
||||
'unit tests run; the Android cold-launch case is unknown)', () {
|
||||
// Trace: SDD-106 §5 state machine; non-Android short-circuit
|
||||
@@ -103,8 +97,7 @@ void main() {
|
||||
svc.dispose();
|
||||
});
|
||||
|
||||
test(
|
||||
'SWE4-UV-041 / SDD-106 §5: inbound permissionStateChanged with '
|
||||
test('SWE4-UV-041 / SDD-106 §5: inbound permissionStateChanged with '
|
||||
'state=Granted transitions recordAudioState to granted and notifies '
|
||||
'listeners', () async {
|
||||
final svc = AndroidPermissionsService(channel: channel)..start();
|
||||
@@ -138,8 +131,7 @@ void main() {
|
||||
svc.dispose();
|
||||
});
|
||||
|
||||
test(
|
||||
'SWE4-UV-041 / SDD-106 §5: inbound permissionStateChanged with '
|
||||
test('SWE4-UV-041 / SDD-106 §5: inbound permissionStateChanged with '
|
||||
'state=Denied transitions recordAudioState to denied', () async {
|
||||
final svc = AndroidPermissionsService(channel: channel)..start();
|
||||
|
||||
@@ -155,8 +147,7 @@ void main() {
|
||||
svc.dispose();
|
||||
});
|
||||
|
||||
test(
|
||||
'SWE4-UV-041 / SDD-106 §3: inbound permissionStateChanged with '
|
||||
test('SWE4-UV-041 / SDD-106 §3: inbound permissionStateChanged with '
|
||||
'state=PermanentlyDenied transitions recordAudioState to '
|
||||
'permanentlyDenied (drives the "open app settings" UX)', () async {
|
||||
final svc = AndroidPermissionsService(channel: channel)..start();
|
||||
@@ -173,8 +164,7 @@ void main() {
|
||||
svc.dispose();
|
||||
});
|
||||
|
||||
test(
|
||||
'SWE4-UV-041 / SDD-106 §5: a malformed state string parses to '
|
||||
test('SWE4-UV-041 / SDD-106 §5: a malformed state string parses to '
|
||||
'unknown (the _parseState default branch)', () async {
|
||||
// Implementation contract: `_parseState` returns `unknown` for any
|
||||
// string outside {Granted, Denied, PermanentlyDenied}.
|
||||
@@ -202,11 +192,9 @@ void main() {
|
||||
svc.dispose();
|
||||
});
|
||||
|
||||
test(
|
||||
'SWE4-UV-041 / SDD-106: inbound permissionStateChanged for a '
|
||||
test('SWE4-UV-041 / SDD-106: inbound permissionStateChanged for a '
|
||||
'permission other than RECORD_AUDIO is ignored (POST_NOTIFICATIONS '
|
||||
'will route through a sibling listenable per the impl note)',
|
||||
() async {
|
||||
'will route through a sibling listenable per the impl note)', () async {
|
||||
final svc = AndroidPermissionsService(channel: channel)..start();
|
||||
|
||||
// Seed a known baseline.
|
||||
@@ -229,14 +217,12 @@ void main() {
|
||||
expect(
|
||||
svc.recordAudioState.value,
|
||||
AndroidRecordAudioPermissionState.denied,
|
||||
reason:
|
||||
'POST_NOTIFICATIONS must not mutate the RECORD_AUDIO listenable',
|
||||
reason: 'POST_NOTIFICATIONS must not mutate the RECORD_AUDIO listenable',
|
||||
);
|
||||
svc.dispose();
|
||||
});
|
||||
|
||||
test(
|
||||
'SWE4-UV-041 / SDD-106: start() is idempotent — calling it twice '
|
||||
test('SWE4-UV-041 / SDD-106: start() is idempotent — calling it twice '
|
||||
'does not double-register the handler and inbound messages still '
|
||||
'fire exactly once', () async {
|
||||
final svc = AndroidPermissionsService(channel: channel)
|
||||
@@ -265,8 +251,7 @@ void main() {
|
||||
svc.dispose();
|
||||
});
|
||||
|
||||
test(
|
||||
'SWE4-UV-041 / SDD-106: stop() removes the handler — subsequent '
|
||||
test('SWE4-UV-041 / SDD-106: stop() removes the handler — subsequent '
|
||||
'inbound messages have no effect on recordAudioState', () async {
|
||||
final svc = AndroidPermissionsService(channel: channel)..start();
|
||||
|
||||
@@ -294,8 +279,7 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
test(
|
||||
'SWE4-UV-041 / SDD-106 §1: ensureRecordAudio() emits an outbound '
|
||||
test('SWE4-UV-041 / SDD-106 §1: ensureRecordAudio() emits an outbound '
|
||||
'requestRecordAudio MethodCall on the channel; when the platform '
|
||||
'stub raises (e.g. handler missing in a debug build) the impl '
|
||||
'catches and resolves to the current cached state', () async {
|
||||
@@ -324,8 +308,11 @@ void main() {
|
||||
.skip(priorOutgoing)
|
||||
.where((c) => c.method == methodRequestRecordAudio)
|
||||
.toList();
|
||||
expect(requests, hasLength(1),
|
||||
reason: 'ensureRecordAudio() must invoke requestRecordAudio');
|
||||
expect(
|
||||
requests,
|
||||
hasLength(1),
|
||||
reason: 'ensureRecordAudio() must invoke requestRecordAudio',
|
||||
);
|
||||
expect(
|
||||
result,
|
||||
AndroidRecordAudioPermissionState.denied,
|
||||
@@ -335,11 +322,9 @@ void main() {
|
||||
svc.dispose();
|
||||
});
|
||||
|
||||
test(
|
||||
'SWE4-UV-041 / SDD-106 §1: ensureRecordAudio() resolves with the '
|
||||
test('SWE4-UV-041 / SDD-106 §1: ensureRecordAudio() resolves with the '
|
||||
'new state when an inbound permissionStateChanged is delivered '
|
||||
'while the request is in-flight (the realistic Kotlin flow)',
|
||||
() async {
|
||||
'while the request is in-flight (the realistic Kotlin flow)', () async {
|
||||
final svc = AndroidPermissionsService(channel: channel)..start();
|
||||
|
||||
// Seed away from the host default (granted on linux) so that the
|
||||
@@ -386,7 +371,34 @@ void main() {
|
||||
});
|
||||
|
||||
test(
|
||||
'SWE4-UV-041 / SDD-106 §3: openAppSettings() emits an outbound '
|
||||
'SWE4-UV-041 / SDD-106 §1: ensureRecordAudio() resolves from the '
|
||||
'platform return value even when the resolved state is unchanged',
|
||||
() async {
|
||||
final svc = AndroidPermissionsService(channel: channel)..start();
|
||||
|
||||
await sendPermissionStateChanged(
|
||||
permission: 'android.permission.RECORD_AUDIO',
|
||||
state: 'Granted',
|
||||
);
|
||||
expect(
|
||||
svc.recordAudioState.value,
|
||||
AndroidRecordAudioPermissionState.granted,
|
||||
);
|
||||
|
||||
outgoingResponder = (call) async => 'Granted';
|
||||
|
||||
final result = await svc.ensureRecordAudio();
|
||||
|
||||
expect(result, AndroidRecordAudioPermissionState.granted);
|
||||
expect(
|
||||
outgoingCalls.where((c) => c.method == methodRequestRecordAudio),
|
||||
hasLength(1),
|
||||
);
|
||||
svc.dispose();
|
||||
},
|
||||
);
|
||||
|
||||
test('SWE4-UV-041 / SDD-106 §3: openAppSettings() emits an outbound '
|
||||
'openAppSettings MethodCall on the channel', () async {
|
||||
final svc = AndroidPermissionsService(channel: channel)..start();
|
||||
|
||||
@@ -399,8 +411,7 @@ void main() {
|
||||
svc.dispose();
|
||||
});
|
||||
|
||||
test(
|
||||
'SWE4-UV-041 / SDD-106: non-Android short-circuit — when channel '
|
||||
test('SWE4-UV-041 / SDD-106: non-Android short-circuit — when channel '
|
||||
'is null, recordAudioState seeds to granted and ensureRecordAudio '
|
||||
'resolves synchronously without touching any channel', () async {
|
||||
final svc = AndroidPermissionsService(channel: null);
|
||||
|
||||
Reference in New Issue
Block a user