Commit d1e6c0b3 authored by Sylvain Berfini's avatar Sylvain Berfini 🎩
Browse files

Added native ringing mode for Android

parent 82378c0a
......@@ -1330,6 +1330,13 @@ static void sound_config_read(LinphoneCore *lc) {
linphone_core_set_ring(lc, get_default_local_ring(lc).c_str());
}
bool_t default_native_ringing_value = FALSE;
#ifdef __ANDROID__
default_native_ringing_value = TRUE;
#endif
lc->native_ringing_enabled = !!lp_config_get_int(lc->config, "sound", "use_native_ringing", default_native_ringing_value);
string defaultRemoteRing = static_cast<PlatformHelpers *>(lc->platform_helper)->getSoundResource(REMOTE_RING_WAV);
tmpbuf = lp_config_get_string(lc->config, "sound", "remote_ring", defaultRemoteRing.c_str());
if (bctbx_file_exist(tmpbuf) == -1){
......@@ -4867,6 +4874,15 @@ const char *linphone_core_get_ring(const LinphoneCore *lc){
return lc->sound_conf.local_ring;
}
void linphone_core_set_native_ringing_enabled(LinphoneCore *core, bool_t enable) {
core->native_ringing_enabled = enable;
lp_config_set_int(core->config, "sound", "use_native_ringing", enable);
}
bool_t linphone_core_is_native_ringing_enabled(const LinphoneCore *core) {
return core->native_ringing_enabled;
}
void linphone_core_set_root_ca(LinphoneCore *lc, const char *path) {
lc->sal->setRootCa(L_C_TO_STRING(path));
if (lc->http_crypto_config) {
......
......@@ -854,6 +854,7 @@ namespace LinphonePrivate {
bool_t push_notification_enabled; \
char * push_notification_param; \
char * push_notification_prid; \
bool_t auto_iterate_enabled;
bool_t auto_iterate_enabled; \
bool_t native_ringing_enabled;
#endif /* _PRIVATE_STRUCTS_H_ */
......@@ -3255,9 +3255,11 @@ LINPHONE_PUBLIC LinphoneStatus linphone_core_set_media_device(LinphoneCore *lc,
LINPHONE_PUBLIC void linphone_core_stop_ringing(LinphoneCore *lc);
/**
* Sets the path to a wav file used for ringing. The file must be a wav 16bit linear. Local ring is disabled if null.
* Sets the path to a wav file used for ringing. The file must be a wav 16bit linear.
* If null, ringing is disable unless #linphone_core_get_use_native_ringing is enabled, in which case we use the device ringtone.
* @param[in] lc #LinphoneCore object
* @param[in] path The path to a wav file to be used for ringing
* @param[in] path The path to a wav file to be used for ringing, null to disable or use device ringing depending on #linphone_core_get_use_native_ringing.
* @maybenil
* @ingroup media_parameters
**/
LINPHONE_PUBLIC void linphone_core_set_ring(LinphoneCore *lc, const char *path);
......@@ -3270,6 +3272,22 @@ LINPHONE_PUBLIC void linphone_core_set_ring(LinphoneCore *lc, const char *path);
**/
LINPHONE_PUBLIC const char *linphone_core_get_ring(const LinphoneCore *lc);
/**
* Sets whether to use the native ringing (Android only).
* @param[in] core #LinphoneCore object
* @param[in] enable True to enable native ringing, false otherwise
* @ingroup media_parameters
**/
LINPHONE_PUBLIC void linphone_core_set_native_ringing_enabled(LinphoneCore *core, bool_t enable);
/**
* Returns whether the native ringing is enabled or not.
* @param[in] core #LinphoneCore object
* @return True if we use the native ringing, false otherwise
* @ingroup media_parameters
**/
LINPHONE_PUBLIC bool_t linphone_core_is_native_ringing_enabled(const LinphoneCore *core);
/**
* Specify whether the tls server certificate must be verified when connecting to a SIP/TLS server.
* @param[in] lc #LinphoneCore object
......
......@@ -422,7 +422,7 @@ void ToneManager::doStartRingtone(const std::shared_ptr<CallSession> &session) {
doStartNamedTone(LinphoneToneCallWaiting);
} else {
MSSndCard *ringcard = lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard;
if (ringcard) {
if (ringcard && !linphone_core_is_native_ringing_enabled(lc)) {
ms_snd_card_set_stream_type(ringcard, MS_SND_CARD_STREAM_RING);
linphone_ringtoneplayer_start(lc->factory, lc->ringtoneplayer, ringcard, lc->sound_conf.local_ring, 2000);
}
......
......@@ -20,32 +20,90 @@
package org.linphone.core.tools.audio;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.media.MediaPlayer;
import android.net.Uri;
import android.provider.Settings;
import androidx.media.AudioAttributesCompat;
import androidx.media.AudioAttributesCompat.Builder;
import androidx.media.AudioManagerCompat;
import androidx.media.AudioFocusRequestCompat;
import java.io.IOException;
import java.io.FileInputStream;
import org.linphone.core.tools.Log;
import org.linphone.core.tools.service.CoreManager;
public class AudioFocusHelper implements OnAudioFocusChangeListener {
public class AudioHelper implements OnAudioFocusChangeListener {
private AudioManager mAudioManager;
private AudioFocusRequestCompat mRingingRequest;
private AudioFocusRequestCompat mCallRequest;
private MediaPlayer mPlayer;
public AudioFocusHelper(Context context) {
public AudioHelper(Context context) {
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mRingingRequest = null;
mCallRequest = null;
Log.i("[Audio Focus Helper] Focus helper created");
Log.i("[Audio Helper] Helper created");
}
public void startRinging(Context context, String ringtone) {
if (mPlayer != null) {
Log.w("[Audio Helper] Already ringing, skipping...");
return;
}
requestRingingAudioFocus();
AudioAttributes audioAttrs = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
mPlayer = new MediaPlayer();
mPlayer.setAudioAttributes(audioAttrs);
if (ringtone == null || ringtone.isEmpty()) {
Log.i("[Audio Helper] Core ringtone path is null, using device ringtone if possible");
ringtone = Settings.System.DEFAULT_RINGTONE_URI.toString();
}
try {
if (ringtone.startsWith("content://")) {
mPlayer.setDataSource(context, Uri.parse(ringtone));
} else {
FileInputStream fis = new FileInputStream(ringtone);
mPlayer.setDataSource(fis.getFD());
fis.close();
}
mPlayer.prepare();
mPlayer.setLooping(true);
mPlayer.start();
Log.i("[Audio Helper] Ringing started");
} catch (IOException e) {
Log.e(e, "[Audio Helper] Cannot set ringtone ", ringtone);
}
}
public void stopRinging() {
if (mPlayer != null) {
releaseRingingAudioFocus();
mPlayer.stop();
mPlayer.release();
mPlayer = null;
Log.i("[Audio Helper] Ringing stopped");
}
}
public void requestRingingAudioFocus() {
if (mRingingRequest != null) {
Log.w("[Audio Focus Helper] Ringing audio focus request still active, skipping");
Log.w("[Audio Helper] Ringing audio focus request still active, skipping");
return;
}
......@@ -54,37 +112,38 @@ public class AudioFocusHelper implements OnAudioFocusChangeListener {
.setContentType(AudioAttributesCompat.CONTENT_TYPE_MUSIC)
.build();
mRingingRequest = new AudioFocusRequestCompat.Builder(AudioManagerCompat.AUDIOFOCUS_GAIN_TRANSIENT)
// Request AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE to prevent incoming call notification to "beep" at the same time we play the ringtone
mRingingRequest = new AudioFocusRequestCompat.Builder(AudioManagerCompat.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
.setAudioAttributes(audioAttrs)
.setOnAudioFocusChangeListener(this)
.build();
int result = AudioManagerCompat.requestAudioFocus(mAudioManager, mRingingRequest);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.i("[Audio Focus Helper] Ringing audio focus request granted");
Log.i("[Audio Helper] Ringing audio focus request granted");
} else if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
Log.w("[Audio Focus Helper] Ringing audio focus request failed");
Log.w("[Audio Helper] Ringing audio focus request failed");
} else if (result == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
Log.w("[Audio Focus Helper] Ringing audio focus request delayed");
Log.w("[Audio Helper] Ringing audio focus request delayed");
}
}
public void releaseRingingAudioFocus() {
if (mRingingRequest != null) {
AudioManagerCompat.abandonAudioFocusRequest(mAudioManager, mRingingRequest);
Log.i("[Audio Focus Helper] Ringing audio focus request abandonned");
Log.i("[Audio Helper] Ringing audio focus request abandonned");
mRingingRequest = null;
}
}
public void requestCallAudioFocus() {
if (mRingingRequest != null) {
Log.w("[Audio Focus Helper] Ringing audio focus request not abandonned, let's do it");
Log.w("[Audio Helper] Ringing audio focus request not abandonned, let's do it");
releaseRingingAudioFocus();
}
if (mCallRequest != null) {
Log.w("[Audio Focus Helper] Call audio focus request still active, skipping");
Log.w("[Audio Helper] Call audio focus request still active, skipping");
return;
}
......@@ -100,19 +159,19 @@ public class AudioFocusHelper implements OnAudioFocusChangeListener {
int result = AudioManagerCompat.requestAudioFocus(mAudioManager, mCallRequest);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.i("[Audio Focus Helper] Call audio focus request granted, setting AudioManager in MODE_IN_COMMUNICATION");
Log.i("[Audio Helper] Call audio focus request granted, setting AudioManager in MODE_IN_COMMUNICATION");
mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
} else if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
Log.w("[Audio Focus Helper] Call audio focus request failed");
Log.w("[Audio Helper] Call audio focus request failed");
} else if (result == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
Log.w("[Audio Focus Helper] Call audio focus request delayed");
Log.w("[Audio Helper] Call audio focus request delayed");
}
}
public void releaseCallAudioFocus() {
if (mCallRequest != null) {
AudioManagerCompat.abandonAudioFocusRequest(mAudioManager, mCallRequest);
Log.i("[Audio Focus Helper] Call audio focus request abandonned, restoring AudioManager mode to MODE_NORMAL");
Log.i("[Audio Helper] Call audio focus request abandonned, restoring AudioManager mode to MODE_NORMAL");
mCallRequest = null;
mAudioManager.setMode(AudioManager.MODE_NORMAL);
}
......@@ -122,18 +181,18 @@ public class AudioFocusHelper implements OnAudioFocusChangeListener {
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
Log.i("[Audio Focus Helper] Focus gained");
Log.i("[Audio Helper] Focus gained");
break;
case AudioManager.AUDIOFOCUS_LOSS:
Log.w("[Audio Focus Helper] Focus lost");
Log.w("[Audio Helper] Focus lost");
if (CoreManager.isReady()) CoreManager.instance().onAudioFocusLost();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
Log.w("[Audio Focus Helper] Focus lost (transient)");
Log.w("[Audio Helper] Focus lost (transient)");
if (CoreManager.isReady()) CoreManager.instance().onAudioFocusLost();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
Log.w("[Audio Focus Helper] Focus lost (transient, can duck)");
Log.w("[Audio Helper] Focus lost (transient, can duck)");
if (CoreManager.isReady()) CoreManager.instance().onAudioFocusLost();
break;
}
......
......@@ -34,7 +34,7 @@ import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub;
import org.linphone.core.tools.Log;
import org.linphone.core.tools.PushNotificationUtils;
import org.linphone.core.tools.audio.AudioFocusHelper;
import org.linphone.core.tools.audio.AudioHelper;
import org.linphone.mediastream.Version;
import java.util.Timer;
......@@ -66,7 +66,7 @@ public class CoreManager {
private Application.ActivityLifecycleCallbacks mActivityCallbacks;
private CoreListenerStub mListener;
private AudioFocusHelper mAudioFocusHelper;
private AudioHelper mAudioHelper;
private native void updatePushNotificationInformation(long ptr, String appId, String token);
......@@ -93,24 +93,36 @@ public class CoreManager {
Log.w("[Core Manager] Push notifications aren't enabled");
}
mAudioFocusHelper = new AudioFocusHelper(mContext);
mAudioHelper = new AudioHelper(mContext);
mListener = new CoreListenerStub() {
@Override
public void onLastCallEnded(Core core) {
Log.i("[Core Manager] Last call ended");
mAudioFocusHelper.releaseRingingAudioFocus();
mAudioFocusHelper.releaseCallAudioFocus();
if (core.isNativeRingingEnabled()) {
mAudioHelper.stopRinging();
} else {
mAudioHelper.releaseRingingAudioFocus();
}
mAudioHelper.releaseCallAudioFocus();
}
@Override
public void onCallStateChanged(Core core, Call call, Call.State state, String message) {
if (state == Call.State.IncomingReceived && core.getCallsNb() == 1) {
Log.i("[Core Manager] Incoming call received, no other call, acquire ringing audio focus");
mAudioFocusHelper.requestRingingAudioFocus();
if (core.isNativeRingingEnabled()) {
Log.i("[Core Manager] Incoming call received, no other call, start ringing");
mAudioHelper.startRinging(mContext, core.getRing());
} else {
Log.i("[Core Manager] Incoming call received, no other call, acquire ringing audio focus");
mAudioHelper.requestRingingAudioFocus();
}
} else if (state == Call.State.Connected && call.getDir() == Call.Dir.Incoming && core.isNativeRingingEnabled()) {
Log.i("[Core Manager] Stop incoming call ringing");
mAudioHelper.stopRinging();
} else if (state == Call.State.OutgoingInit || state == Call.State.StreamsRunning) {
Log.i("[Core Manager] Call active, ensure audio focus granted");
mAudioFocusHelper.requestCallAudioFocus();
mAudioHelper.requestCallAudioFocus();
}
}
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment