I'm developing a mini native (Java) SDK to turn ON/OFF speaker for a react-native app for calls , because React-Native Incall Manager doesn't work properly on Android 12.
Module is working good on Android 11, but since setSpeakerOn method from AudioManager is deprecated for newest SDK, on Android 12 there are some issues. I took the code snippet from official docs and adapted it. Even if boolean
result = audioManager.setCommunicationDevice(device) returns TRUE , it doesn't change the speaker state.
Permission
android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
AudioModule.java
public class AudioModule extends ReactContextBaseJavaModule {
private AudioManager audioManager;
private static final String TAG = "AudioModule";
private static ReactApplicationContext reactContext;
AudioModule(ReactApplicationContext context) {
super(context);
reactContext = context;
audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE));
}
@Override
public String getName() {
return "AudioModule";
}
@ReactMethod
public void setSpeakerState(Boolean speakerState) {
try {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
if (speakerState) {
setCommunicationDevice(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
} else {
audioManager.clearCommunicationDevice(); //turn off speaker
}
} else {
audioManager.setMode(speakerState ? AudioManager.MODE_IN_COMMUNICATION : AudioManager.MODE_NORMAL);
audioManager.setSpeakerphoneOn(speakerState);
}
Toast.makeText(reactContext, "speakerStatus " + audioManager.isSpeakerphoneOn(), Toast.LENGTH_LONG).show();
} catch (Exception e) {
Log.d(TAG, "AudioModule setSpeaker", e);
}
}
@RequiresApi(api = Build.VERSION_CODES.S)
public void setCommunicationDevice(Integer targetDeviceType) {
List<AudioDeviceInfo> audioDevices = audioManager.getAvailableCommunicationDevices();
for (AudioDeviceInfo device : audioDevices) {
if (device.getType() == targetDeviceType) {
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION );
boolean result = audioManager.setCommunicationDevice(device);
break;
}
}
}
Note: Native Module for React-Native is working properly on Android 11 OS and is showing Toast on Android 12 and 13 (tested on physical devices)
UPDATED: Tried also with IncallService interface: import android.telecom.InCallService;
public class CallService extends InCallService {
private static CallService sInstance;
CallService() {
sInstance = this;
}
public static CallService getInstance() {
return sInstance;
}
}
Method called
@ReactMethod
public void toggleSpeaker() {
boolean isSpeakerOn = audioManager.isSpeakerphoneOn();
int earpiece = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
int speaker = CallAudioState.ROUTE_SPEAKER;
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.P) {
callService.getInstance().setAudioRoute(isSpeakerOn ? earpiece : speaker);
} else {
audioManager.setSpeakerphoneOn(!isSpeakerOn);
}
Toast.makeText(reactContext, "speakerStatus " + audioManager.isSpeakerphoneOn(), Toast.LENGTH_LONG).show();
}
Logcat returns the next message:
android.hardware.audio.service: Failed to fetch the lookup information of the device