You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 13下使用Xamarin激活扬声器的有效方案求助

Android 13 + Xamarin 扬声器激活有效方案

1. 权限配置与动态申请

Android 13下必须确保拥有以下核心权限:

  • MODIFY_AUDIO_SETTINGS:修改音频路由的必要权限
  • READ_PHONE_STATE:获取通话状态,确保在正确时机切换扬声器

在AndroidManifest.xml中添加权限声明:

<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

运行时动态申请权限(在Activity/Fragment中实现):

private async Task RequestRequiredPermissions()
{
    var readPhoneStatus = await Permissions.RequestAsync<Permissions.ReadPhoneState>();
    if (readPhoneStatus != PermissionStatus.Granted)
    {
        // 处理权限拒绝逻辑,如提示用户开启权限
        return;
    }

    var modifyAudioStatus = await Permissions.RequestAsync<Permissions.ModifyAudioSettings>();
    if (modifyAudioStatus != PermissionStatus.Granted)
    {
        // 处理权限拒绝逻辑
        return;
    }
}

2. 监听通话状态+精准切换扬声器

直接按钮点击切换无效的核心原因是时机不对——必须在通话进入接通状态后操作。以下是完整实现:

private AudioManager _audioManager;
private PhoneStateListener _callStateListener;
private TelephonyManager _telephonyManager;

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);
    _audioManager = (AudioManager)GetSystemService(AudioService);
    _telephonyManager = (TelephonyManager)GetSystemService(TelephonyService);

    // 初始化通话状态监听器
    _callStateListener = new CallStateMonitor(this);
    _telephonyManager.Listen(_callStateListener, PhoneStateListenerFlags.CallState);

    // 绑定扬声器切换按钮
    var toggleSpeakerBtn = FindViewById<Button>(Resource.Id.btn_toggle_speaker);
    toggleSpeakerBtn.Click += ToggleSpeakerBtn_Click;
}

private void ToggleSpeakerBtn_Click(object sender, EventArgs e)
{
    ToggleSpeakerRoute();
}

private void ToggleSpeakerRoute()
{
    // 确保当前处于通话状态
    if (_telephonyManager.CallState != CallState.Offhook) return;

    if (_audioManager.IsSpeakerphoneOn)
    {
        _audioManager.SetSpeakerphoneOn(false);
        _audioManager.Mode = Mode.InCall;
    }
    else
    {
        _audioManager.SetSpeakerphoneOn(true);
        _audioManager.Mode = Mode.InCall;
    }
}

// 自定义通话状态监听器,自动在接通时打开扬声器(可选)
private class CallStateMonitor : PhoneStateListener
{
    private readonly MainActivity _hostActivity;

    public CallStateMonitor(MainActivity activity)
    {
        _hostActivity = activity;
    }

    public override void OnCallStateChanged(CallState state, string incomingNumber)
    {
        base.OnCallStateChanged(state, incomingNumber);
        switch (state)
        {
            case CallState.Offhook:
                // 通话接通,强制打开扬声器
                _hostActivity._audioManager.SetSpeakerphoneOn(true);
                _hostActivity._audioManager.Mode = Mode.InCall;
                break;
            case CallState.Idle:
                // 通话结束,重置音频状态
                _hostActivity._audioManager.SetSpeakerphoneOn(false);
                _hostActivity._audioManager.Mode = Mode.Normal;
                break;
        }
    }
}

protected override void OnDestroy()
{
    base.OnDestroy();
    // 取消监听,避免内存泄漏
    _telephonyManager.Listen(_callStateListener, PhoneStateListenerFlags.None);
}

3. 关键注意事项

  • 权限必须动态申请:Android 13中READ_PHONE_STATE属于危险权限,静态声明后仍需运行时申请,否则无法获取通话状态
  • 音频模式必须设置:切换扬声器时必须将AudioManager.Mode设为Mode.InCall,否则系统会自动覆盖音频路由
  • 必须在真实设备测试:模拟器无法模拟真实通话的音频路由逻辑,务必用实体机测试
  • 避免提前操作:只有通话进入Offhook(接通)状态后,切换扬声器的操作才会生效

内容的提问来源于stack exchange,提问作者Golf Man

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 14:02:22