Compare commits
2 Commits
7339d5982e
...
936250829b
Author | SHA1 | Date | |
---|---|---|---|
936250829b | |||
6c5ce1beab |
@ -165,6 +165,32 @@ class MainActivity : FlutterActivity() {
|
|||||||
Log.d(TAG, "getCallState called, returning: $stateStr")
|
Log.d(TAG, "getCallState called, returning: $stateStr")
|
||||||
result.success(stateStr)
|
result.success(stateStr)
|
||||||
}
|
}
|
||||||
|
"muteCall" -> {
|
||||||
|
val mute = call.argument<Boolean>("mute") ?: false
|
||||||
|
val success = MyInCallService.currentCall?.let {
|
||||||
|
MyInCallService.toggleMute(mute)
|
||||||
|
} ?: false
|
||||||
|
if (success) {
|
||||||
|
Log.d(TAG, "Mute call set to $mute")
|
||||||
|
result.success(mapOf("status" to "success"))
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "No active call or failed to mute")
|
||||||
|
result.error("MUTE_FAILED", "No active call or failed to mute", null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"speakerCall" -> {
|
||||||
|
val speaker = call.argument<Boolean>("speaker") ?: false
|
||||||
|
val success = MyInCallService.currentCall?.let {
|
||||||
|
MyInCallService.toggleSpeaker(speaker)
|
||||||
|
} ?: false
|
||||||
|
if (success) {
|
||||||
|
Log.d(TAG, "Speaker call set to $speaker")
|
||||||
|
result.success(mapOf("status" to "success"))
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "No active call or failed to set speaker")
|
||||||
|
result.error("SPEAKER_FAILED", "No active call or failed to set speaker", null)
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> result.notImplemented()
|
else -> result.notImplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,11 @@ import android.app.NotificationManager
|
|||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.media.AudioManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.telecom.Call
|
import android.telecom.Call
|
||||||
import android.telecom.InCallService
|
import android.telecom.InCallService
|
||||||
|
import android.telecom.CallAudioState
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import com.icing.dialer.activities.MainActivity
|
import com.icing.dialer.activities.MainActivity
|
||||||
@ -22,6 +24,43 @@ class MyInCallService : InCallService() {
|
|||||||
private const val NOTIFICATION_CHANNEL_ID = "incoming_call_channel"
|
private const val NOTIFICATION_CHANNEL_ID = "incoming_call_channel"
|
||||||
private const val NOTIFICATION_ID = 1
|
private const val NOTIFICATION_ID = 1
|
||||||
var wasPhoneLocked: Boolean = false
|
var wasPhoneLocked: Boolean = false
|
||||||
|
private var instance: MyInCallService? = null
|
||||||
|
|
||||||
|
fun toggleMute(mute: Boolean): Boolean {
|
||||||
|
return instance?.let { service ->
|
||||||
|
try {
|
||||||
|
val audioManager = service.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||||
|
audioManager.isMicrophoneMute = mute
|
||||||
|
Log.d(TAG, "Set microphone mute state to $mute")
|
||||||
|
channel?.invokeMethod("audioStateChanged", mapOf(
|
||||||
|
"muted" to mute,
|
||||||
|
"speaker" to audioManager.isSpeakerphoneOn
|
||||||
|
))
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to set mute state: $e")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggleSpeaker(speaker: Boolean): Boolean {
|
||||||
|
return instance?.let { service ->
|
||||||
|
try {
|
||||||
|
val audioManager = service.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||||
|
audioManager.isSpeakerphoneOn = speaker
|
||||||
|
Log.d(TAG, "Set speakerphone state to $speaker")
|
||||||
|
channel?.invokeMethod("audioStateChanged", mapOf(
|
||||||
|
"muted" to audioManager.isMicrophoneMute,
|
||||||
|
"speaker" to speaker
|
||||||
|
))
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to set speaker state: $e")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} ?: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val callCallback = object : Call.Callback() {
|
private val callCallback = object : Call.Callback() {
|
||||||
@ -60,6 +99,7 @@ class MyInCallService : InCallService() {
|
|||||||
|
|
||||||
override fun onCallAdded(call: Call) {
|
override fun onCallAdded(call: Call) {
|
||||||
super.onCallAdded(call)
|
super.onCallAdded(call)
|
||||||
|
instance = this
|
||||||
currentCall = call
|
currentCall = call
|
||||||
val stateStr = when (call.state) {
|
val stateStr = when (call.state) {
|
||||||
Call.STATE_DIALING -> "dialing"
|
Call.STATE_DIALING -> "dialing"
|
||||||
@ -90,13 +130,18 @@ class MyInCallService : InCallService() {
|
|||||||
"wasPhoneLocked" to wasPhoneLocked
|
"wasPhoneLocked" to wasPhoneLocked
|
||||||
))
|
))
|
||||||
currentCall = null
|
currentCall = null
|
||||||
|
instance = null
|
||||||
cancelNotification()
|
cancelNotification()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallAudioStateChanged(state: android.telecom.CallAudioState) {
|
override fun onCallAudioStateChanged(state: android.telecom.CallAudioState) {
|
||||||
super.onCallAudioStateChanged(state)
|
super.onCallAudioStateChanged(state)
|
||||||
Log.d(TAG, "Audio state changed: route=${state.route}")
|
Log.d(TAG, "Audio state changed: route=${state.route}, muted=${state.isMuted}")
|
||||||
channel?.invokeMethod("audioStateChanged", mapOf("route" to state.route))
|
channel?.invokeMethod("audioStateChanged", mapOf(
|
||||||
|
"route" to state.route,
|
||||||
|
"muted" to state.isMuted,
|
||||||
|
"speaker" to (state.route == CallAudioState.ROUTE_SPEAKER)
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showIncomingCallScreen(phoneNumber: String) {
|
private fun showIncomingCallScreen(phoneNumber: String) {
|
||||||
|
@ -25,7 +25,7 @@ class _CallPageState extends State<CallPage> {
|
|||||||
final ObfuscateService _obfuscateService = ObfuscateService();
|
final ObfuscateService _obfuscateService = ObfuscateService();
|
||||||
final CallService _callService = CallService();
|
final CallService _callService = CallService();
|
||||||
bool isMuted = false;
|
bool isMuted = false;
|
||||||
bool isSpeakerOn = false;
|
bool isSpeaker = false;
|
||||||
bool isKeypadVisible = false;
|
bool isKeypadVisible = false;
|
||||||
bool icingProtocolOk = true;
|
bool icingProtocolOk = true;
|
||||||
String _typedDigits = "";
|
String _typedDigits = "";
|
||||||
@ -102,16 +102,58 @@ class _CallPageState extends State<CallPage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _toggleMute() {
|
void _toggleMute() async {
|
||||||
setState(() {
|
try {
|
||||||
isMuted = !isMuted;
|
print('CallPage: Toggling mute, current state: $isMuted');
|
||||||
});
|
final result = await _callService.muteCall(context, mute: !isMuted);
|
||||||
|
print('CallPage: Mute call result: $result');
|
||||||
|
if (mounted && result['status'] == 'success') {
|
||||||
|
setState(() {
|
||||||
|
isMuted = !isMuted;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
print('CallPage: Failed to toggle mute: ${result['message']}');
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text('Failed to toggle mute: ${result['message']}')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('CallPage: Error toggling mute: $e');
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Error toggling mute: $e')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _toggleSpeaker() {
|
Future<void> _toggleSpeaker() async {
|
||||||
setState(() {
|
try {
|
||||||
isSpeakerOn = !isSpeakerOn;
|
print('CallPage: Toggling speaker, current state: $isSpeaker');
|
||||||
});
|
final result =
|
||||||
|
await _callService.speakerCall(context, speaker: !isSpeaker);
|
||||||
|
print('CallPage: Speaker call result: $result');
|
||||||
|
if (result['status'] == 'success') {
|
||||||
|
setState(() {
|
||||||
|
isSpeaker = !isSpeaker;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text('Failed to toggle speaker: ${result['message']}')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('CallPage: Error toggling speaker: $e');
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Error toggling speaker: $e')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _toggleKeypad() {
|
void _toggleKeypad() {
|
||||||
@ -147,7 +189,8 @@ class _CallPageState extends State<CallPage> {
|
|||||||
final double nameFontSize = isKeypadVisible ? 24.0 : 24.0;
|
final double nameFontSize = isKeypadVisible ? 24.0 : 24.0;
|
||||||
final double statusFontSize = isKeypadVisible ? 16.0 : 16.0;
|
final double statusFontSize = isKeypadVisible ? 16.0 : 16.0;
|
||||||
|
|
||||||
print('CallPage: Building UI, _callStatus: $_callStatus, route: ${ModalRoute.of(context)?.settings.name ?? "unknown"}');
|
print(
|
||||||
|
'CallPage: Building UI, _callStatus: $_callStatus, route: ${ModalRoute.of(context)?.settings.name ?? "unknown"}');
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Container(
|
body: Container(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
@ -235,7 +278,8 @@ class _CallPageState extends State<CallPage> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
onPressed: _toggleKeypad,
|
onPressed: _toggleKeypad,
|
||||||
icon: const Icon(Icons.close, color: Colors.white),
|
icon:
|
||||||
|
const Icon(Icons.close, color: Colors.white),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -298,7 +342,9 @@ class _CallPageState extends State<CallPage> {
|
|||||||
onPressed: _toggleMute,
|
onPressed: _toggleMute,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
isMuted ? Icons.mic_off : Icons.mic,
|
isMuted ? Icons.mic_off : Icons.mic,
|
||||||
color: Colors.white,
|
color: isMuted
|
||||||
|
? Colors.amber
|
||||||
|
: Colors.white,
|
||||||
size: 32,
|
size: 32,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -333,10 +379,10 @@ class _CallPageState extends State<CallPage> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
onPressed: _toggleSpeaker,
|
onPressed: _toggleSpeaker,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
isSpeakerOn
|
isSpeaker
|
||||||
? Icons.volume_up
|
? Icons.volume_up
|
||||||
: Icons.volume_off,
|
: Icons.volume_off,
|
||||||
color: isSpeakerOn
|
color: isSpeaker
|
||||||
? Colors.amber
|
? Colors.amber
|
||||||
: Colors.white,
|
: Colors.white,
|
||||||
size: 32,
|
size: 32,
|
||||||
@ -425,4 +471,4 @@ class _CallPageState extends State<CallPage> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,6 +144,42 @@ class CallService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> muteCall(BuildContext context, {required bool mute}) async {
|
||||||
|
try {
|
||||||
|
print('CallService: Toggling mute to $mute');
|
||||||
|
final result = await _channel.invokeMethod('muteCall', {'mute': mute});
|
||||||
|
print('CallService: muteCall result: $result');
|
||||||
|
final resultMap = Map<String, dynamic>.from(result as Map);
|
||||||
|
if (resultMap['status'] != 'success') {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Failed to toggle mute')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return resultMap;
|
||||||
|
} catch (e) {
|
||||||
|
print('CallService: Error toggling mute: $e');
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Error toggling mute: $e')),
|
||||||
|
);
|
||||||
|
return {'status': 'error', 'message': e.toString()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> speakerCall(BuildContext context, {required bool speaker}) async {
|
||||||
|
try {
|
||||||
|
print('CallService: Toggling speaker to $speaker');
|
||||||
|
final result = await _channel.invokeMethod('speakerCall', {'speaker': speaker});
|
||||||
|
print('CallService: speakerCall result: $result');
|
||||||
|
return Map<String, dynamic>.from(result);
|
||||||
|
} catch (e) {
|
||||||
|
print('CallService: Error toggling speaker: $e');
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Failed to toggle speaker: $e')),
|
||||||
|
);
|
||||||
|
return {'status': 'error', 'message': e.toString()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_callStateController.close();
|
_callStateController.close();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user