Compare commits
5 commits
f092430c84
...
0f581f889a
Author | SHA1 | Date | |
---|---|---|---|
Peter Cai | 0f581f889a | ||
Peter Cai | 9cb59cc735 | ||
Peter Cai | d3078dfd8b | ||
Peter Cai | 0872f24128 | ||
Peter Cai | 6ff1a2366f |
|
@ -1,292 +1,328 @@
|
||||||
package com.cheogram.android;
|
package com.cheogram.android;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
|
import android.graphics.drawable.Icon;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.telecom.CallAudioState;
|
||||||
|
import android.telecom.Connection;
|
||||||
|
import android.telecom.ConnectionRequest;
|
||||||
|
import android.telecom.DisconnectCause;
|
||||||
|
import android.telecom.PhoneAccountHandle;
|
||||||
|
import android.telecom.StatusHints;
|
||||||
|
import android.telecom.TelecomManager;
|
||||||
|
import android.telephony.PhoneNumberUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.intentfilter.androidpermissions.NotificationSettings;
|
||||||
|
import com.intentfilter.androidpermissions.PermissionManager;
|
||||||
|
import com.intentfilter.androidpermissions.models.DeniedPermissions;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
import java.util.Vector;
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
|
|
||||||
import android.telecom.CallAudioState;
|
|
||||||
import android.telecom.Connection;
|
|
||||||
import android.telecom.ConnectionRequest;
|
|
||||||
import android.telecom.DisconnectCause;
|
|
||||||
import android.telecom.PhoneAccount;
|
|
||||||
import android.telecom.PhoneAccountHandle;
|
|
||||||
import android.telecom.StatusHints;
|
|
||||||
import android.telecom.TelecomManager;
|
|
||||||
import android.telephony.PhoneNumberUtils;
|
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import android.graphics.drawable.Icon;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.Parcel;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.intentfilter.androidpermissions.PermissionManager;
|
|
||||||
import com.intentfilter.androidpermissions.NotificationSettings;
|
|
||||||
import com.intentfilter.androidpermissions.models.DeniedPermissions;
|
|
||||||
import io.michaelrocks.libphonenumber.android.NumberParseException;
|
|
||||||
|
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.services.AppRTCAudioManager;
|
import eu.siacs.conversations.services.AppRTCAudioManager;
|
||||||
import eu.siacs.conversations.services.AvatarService;
|
import eu.siacs.conversations.services.AvatarService;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder;
|
|
||||||
import eu.siacs.conversations.services.XmppConnectionService;
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
|
import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder;
|
||||||
import eu.siacs.conversations.ui.RtpSessionActivity;
|
import eu.siacs.conversations.ui.RtpSessionActivity;
|
||||||
import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
|
import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
|
||||||
import eu.siacs.conversations.xmpp.Jid;
|
import eu.siacs.conversations.xmpp.Jid;
|
||||||
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
|
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
|
||||||
import eu.siacs.conversations.xmpp.jingle.Media;
|
import eu.siacs.conversations.xmpp.jingle.Media;
|
||||||
import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
|
import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
|
||||||
|
import io.michaelrocks.libphonenumber.android.NumberParseException;
|
||||||
|
|
||||||
public class ConnectionService extends android.telecom.ConnectionService {
|
public class ConnectionService extends android.telecom.ConnectionService {
|
||||||
public XmppConnectionService xmppConnectionService = null;
|
public XmppConnectionService xmppConnectionService = null;
|
||||||
protected ServiceConnection mConnection = new ServiceConnection() {
|
protected ServiceConnection mConnection = new ServiceConnection() {
|
||||||
@Override
|
@Override
|
||||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||||
XmppConnectionBinder binder = (XmppConnectionBinder) service;
|
XmppConnectionBinder binder = (XmppConnectionBinder) service;
|
||||||
xmppConnectionService = binder.getService();
|
xmppConnectionService = binder.getService();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onServiceDisconnected(ComponentName arg0) {
|
public void onServiceDisconnected(ComponentName arg0) {
|
||||||
xmppConnectionService = null;
|
xmppConnectionService = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
private PermissionManager mPermissionManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
// From XmppActivity.connectToBackend
|
mPermissionManager = PermissionManager.getInstance(this);
|
||||||
Intent intent = new Intent(this, XmppConnectionService.class);
|
mPermissionManager.setNotificationSettings(
|
||||||
intent.setAction("ui");
|
new NotificationSettings.Builder()
|
||||||
try {
|
.withMessage(R.string.microphone_permission_for_call)
|
||||||
startService(intent);
|
.withSmallIcon(R.drawable.ic_notification).build()
|
||||||
} catch (IllegalStateException e) {
|
);
|
||||||
Log.w("com.cheogram.android.ConnectionService", "unable to start service from " + getClass().getSimpleName());
|
|
||||||
}
|
|
||||||
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
// From XmppActivity.connectToBackend
|
||||||
public void onDestroy() {
|
Intent intent = new Intent(this, XmppConnectionService.class);
|
||||||
unbindService(mConnection);
|
intent.setAction("ui");
|
||||||
}
|
try {
|
||||||
|
startService(intent);
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
Log.w("com.cheogram.android.ConnectionService", "unable to start service from " + getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Connection onCreateOutgoingConnection(
|
public void onDestroy() {
|
||||||
PhoneAccountHandle phoneAccountHandle,
|
unbindService(mConnection);
|
||||||
ConnectionRequest request
|
}
|
||||||
) {
|
|
||||||
String[] gateway = phoneAccountHandle.getId().split("/", 2);
|
|
||||||
|
|
||||||
String rawTel = request.getAddress().getSchemeSpecificPart();
|
@Override
|
||||||
String postDial = PhoneNumberUtils.extractPostDialPortion(rawTel);
|
public Connection onCreateOutgoingConnection(
|
||||||
|
PhoneAccountHandle phoneAccountHandle,
|
||||||
|
ConnectionRequest request
|
||||||
|
) {
|
||||||
|
String[] gateway = phoneAccountHandle.getId().split("/", 2);
|
||||||
|
|
||||||
String tel = PhoneNumberUtils.extractNetworkPortion(rawTel);
|
String rawTel = request.getAddress().getSchemeSpecificPart();
|
||||||
try {
|
String postDial = PhoneNumberUtils.extractPostDialPortion(rawTel);
|
||||||
tel = PhoneNumberUtilWrapper.normalize(this, tel);
|
|
||||||
} catch (NumberParseException e) {
|
|
||||||
return Connection.createFailedConnection(
|
|
||||||
new DisconnectCause(DisconnectCause.ERROR)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xmppConnectionService.getJingleConnectionManager().isBusy() != null) {
|
String tel = PhoneNumberUtils.extractNetworkPortion(rawTel);
|
||||||
return Connection.createFailedConnection(
|
try {
|
||||||
new DisconnectCause(DisconnectCause.BUSY)
|
tel = PhoneNumberUtilWrapper.normalize(this, tel);
|
||||||
);
|
} catch (NumberParseException e) {
|
||||||
}
|
return Connection.createFailedConnection(
|
||||||
|
new DisconnectCause(DisconnectCause.ERROR)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Account account = xmppConnectionService.findAccountByJid(Jid.of(gateway[0]));
|
if (xmppConnectionService.getJingleConnectionManager().isBusy() != null) {
|
||||||
Jid with = Jid.ofLocalAndDomain(tel, gateway[1]);
|
return Connection.createFailedConnection(
|
||||||
CheogramConnection connection = new CheogramConnection(account, with, postDial);
|
new DisconnectCause(DisconnectCause.BUSY)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
PermissionManager permissionManager = PermissionManager.getInstance(this);
|
Account account = xmppConnectionService.findAccountByJid(Jid.of(gateway[0]));
|
||||||
permissionManager.setNotificationSettings(
|
Jid with = Jid.ofLocalAndDomain(tel, gateway[1]);
|
||||||
new NotificationSettings.Builder()
|
CheogramConnection connection = new CheogramConnection(account, with, postDial);
|
||||||
.withMessage(R.string.microphone_permission_for_call)
|
|
||||||
.withSmallIcon(R.drawable.ic_notification).build()
|
|
||||||
);
|
|
||||||
|
|
||||||
Set<String> permissions = new HashSet();
|
Set<String> permissions = new HashSet<>();
|
||||||
permissions.add(Manifest.permission.RECORD_AUDIO);
|
permissions.add(Manifest.permission.RECORD_AUDIO);
|
||||||
permissionManager.checkPermissions(permissions, new PermissionManager.PermissionRequestListener() {
|
mPermissionManager.checkPermissions(permissions, new PermissionManager.PermissionRequestListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onPermissionGranted() {
|
public void onPermissionGranted() {
|
||||||
connection.setSessionId(xmppConnectionService.getJingleConnectionManager().proposeJingleRtpSession(
|
connection.setSessionId(xmppConnectionService.getJingleConnectionManager().proposeJingleRtpSession(
|
||||||
account,
|
account,
|
||||||
with,
|
with,
|
||||||
ImmutableSet.of(Media.AUDIO)
|
ImmutableSet.of(Media.AUDIO)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPermissionDenied(DeniedPermissions deniedPermissions) {
|
public void onPermissionDenied(DeniedPermissions deniedPermissions) {
|
||||||
connection.setDisconnected(new DisconnectCause(DisconnectCause.ERROR));
|
connection.setDisconnected(new DisconnectCause(DisconnectCause.ERROR));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connection.setInitializing();
|
connection.setInitializing();
|
||||||
connection.setAddress(
|
connection.setAddress(
|
||||||
Uri.fromParts("tel", tel, null), // Normalized tel as tel: URI
|
Uri.fromParts("tel", tel, null), // Normalized tel as tel: URI
|
||||||
TelecomManager.PRESENTATION_ALLOWED
|
TelecomManager.PRESENTATION_ALLOWED
|
||||||
);
|
);
|
||||||
|
|
||||||
xmppConnectionService.setOnRtpConnectionUpdateListener(
|
xmppConnectionService.setOnRtpConnectionUpdateListener(
|
||||||
(XmppConnectionService.OnJingleRtpConnectionUpdate) connection
|
(XmppConnectionService.OnJingleRtpConnectionUpdate) connection
|
||||||
);
|
);
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CheogramConnection extends Connection implements XmppConnectionService.OnJingleRtpConnectionUpdate {
|
@Override
|
||||||
protected Account account;
|
public Connection onCreateIncomingConnection(PhoneAccountHandle handle, ConnectionRequest request) {
|
||||||
protected Jid with;
|
Bundle extras = request.getExtras();
|
||||||
protected String sessionId = null;
|
String accountJid = extras.getString("account");
|
||||||
protected Stack<String> postDial = new Stack();
|
String withJid = extras.getString("with");
|
||||||
protected Icon gatewayIcon;
|
String sessionId = extras.getString("sessionId");
|
||||||
protected WeakReference<JingleRtpConnection> rtpConnection = null;
|
|
||||||
|
|
||||||
CheogramConnection(Account account, Jid with, String postDialString) {
|
Account account = xmppConnectionService.findAccountByJid(Jid.of(accountJid));
|
||||||
super();
|
Jid with = Jid.of(withJid);
|
||||||
this.account = account;
|
|
||||||
this.with = with;
|
|
||||||
|
|
||||||
gatewayIcon = Icon.createWithBitmap(xmppConnectionService.getAvatarService().get(
|
CheogramConnection connection = new CheogramConnection(account, with, null);
|
||||||
account.getRoster().getContact(Jid.of(with.getDomain())),
|
connection.setSessionId(sessionId);
|
||||||
AvatarService.getSystemUiAvatarSize(xmppConnectionService),
|
connection.setAddress(
|
||||||
false
|
Uri.fromParts("tel", with.getLocal(), null),
|
||||||
));
|
TelecomManager.PRESENTATION_ALLOWED
|
||||||
|
);
|
||||||
|
connection.setRinging();
|
||||||
|
|
||||||
if (postDialString != null) {
|
xmppConnectionService.setOnRtpConnectionUpdateListener(connection);
|
||||||
for (int i = postDialString.length() - 1; i >= 0; i--) {
|
|
||||||
postDial.push("" + postDialString.charAt(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setCallerDisplayName(
|
return connection;
|
||||||
account.getDisplayName(),
|
}
|
||||||
TelecomManager.PRESENTATION_ALLOWED
|
|
||||||
);
|
|
||||||
setAudioModeIsVoip(true);
|
|
||||||
setConnectionCapabilities(
|
|
||||||
Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSessionId(final String sessionId) {
|
public class CheogramConnection extends Connection implements XmppConnectionService.OnJingleRtpConnectionUpdate {
|
||||||
this.sessionId = sessionId;
|
protected Account account;
|
||||||
}
|
protected Jid with;
|
||||||
|
protected String sessionId = null;
|
||||||
|
protected Stack<String> postDial = new Stack<>();
|
||||||
|
protected Icon gatewayIcon;
|
||||||
|
protected WeakReference<JingleRtpConnection> rtpConnection = null;
|
||||||
|
|
||||||
@Override
|
CheogramConnection(Account account, Jid with, String postDialString) {
|
||||||
public void onJingleRtpConnectionUpdate(final Account account, final Jid with, final String sessionId, final RtpEndUserState state) {
|
super();
|
||||||
if (sessionId == null || !sessionId.equals(this.sessionId)) return;
|
this.account = account;
|
||||||
if (rtpConnection == null) {
|
this.with = with;
|
||||||
this.with = with; // Store full JID of connection
|
|
||||||
rtpConnection = xmppConnectionService.getJingleConnectionManager().findJingleRtpConnection(account, with, sessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
setStatusHints(new StatusHints(null, gatewayIcon, null));
|
gatewayIcon = Icon.createWithBitmap(xmppConnectionService.getAvatarService().get(
|
||||||
|
account.getRoster().getContact(Jid.of(with.getDomain())),
|
||||||
|
AvatarService.getSystemUiAvatarSize(xmppConnectionService),
|
||||||
|
false
|
||||||
|
));
|
||||||
|
|
||||||
if (state == RtpEndUserState.FINDING_DEVICE) {
|
if (postDialString != null) {
|
||||||
setInitialized();
|
for (int i = postDialString.length() - 1; i >= 0; i--) {
|
||||||
} else if (state == RtpEndUserState.RINGING) {
|
postDial.push("" + postDialString.charAt(i));
|
||||||
setDialing();
|
}
|
||||||
} else if (state == RtpEndUserState.CONNECTED) {
|
}
|
||||||
xmppConnectionService.setDiallerIntegrationActive(true);
|
|
||||||
setActive();
|
|
||||||
|
|
||||||
postDial();
|
setCallerDisplayName(
|
||||||
} else if (state == RtpEndUserState.DECLINED_OR_BUSY) {
|
account.getDisplayName(),
|
||||||
setDisconnected(new DisconnectCause(DisconnectCause.BUSY));
|
TelecomManager.PRESENTATION_ALLOWED
|
||||||
} else if (state == RtpEndUserState.ENDED) {
|
);
|
||||||
setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
|
setAudioModeIsVoip(true);
|
||||||
} else if (state == RtpEndUserState.RETRACTED) {
|
setConnectionCapabilities(
|
||||||
setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
|
Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION
|
||||||
} else if (RtpSessionActivity.END_CARD.contains(state)) {
|
);
|
||||||
setDisconnected(new DisconnectCause(DisconnectCause.ERROR));
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public void setSessionId(final String sessionId) {
|
||||||
public void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set<AppRTCAudioManager.AudioDevice> availableAudioDevices) {
|
this.sessionId = sessionId;
|
||||||
switch(selectedAudioDevice) {
|
}
|
||||||
case SPEAKER_PHONE:
|
|
||||||
setAudioRoute(CallAudioState.ROUTE_SPEAKER);
|
|
||||||
case WIRED_HEADSET:
|
|
||||||
setAudioRoute(CallAudioState.ROUTE_WIRED_HEADSET);
|
|
||||||
case EARPIECE:
|
|
||||||
setAudioRoute(CallAudioState.ROUTE_EARPIECE);
|
|
||||||
case BLUETOOTH:
|
|
||||||
setAudioRoute(CallAudioState.ROUTE_BLUETOOTH);
|
|
||||||
default:
|
|
||||||
setAudioRoute(CallAudioState.ROUTE_WIRED_OR_EARPIECE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisconnect() {
|
public void onJingleRtpConnectionUpdate(final Account account, final Jid with, final String sessionId, final RtpEndUserState state) {
|
||||||
if (rtpConnection == null || rtpConnection.get() == null) {
|
if (sessionId == null || !sessionId.equals(this.sessionId)) return;
|
||||||
xmppConnectionService.getJingleConnectionManager().retractSessionProposal(account, with.asBareJid());
|
if (rtpConnection == null) {
|
||||||
} else {
|
this.with = with; // Store full JID of connection
|
||||||
rtpConnection.get().endCall();
|
rtpConnection = xmppConnectionService.getJingleConnectionManager().findJingleRtpConnection(account, with, sessionId);
|
||||||
}
|
}
|
||||||
destroy();
|
|
||||||
xmppConnectionService.setDiallerIntegrationActive(false);
|
|
||||||
xmppConnectionService.removeRtpConnectionUpdateListener(
|
|
||||||
(XmppConnectionService.OnJingleRtpConnectionUpdate) this
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
setStatusHints(new StatusHints(null, gatewayIcon, null));
|
||||||
public void onAbort() {
|
|
||||||
onDisconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if (state == RtpEndUserState.FINDING_DEVICE) {
|
||||||
public void onPlayDtmfTone(char c) {
|
setInitialized();
|
||||||
rtpConnection.get().applyDtmfTone("" + c);
|
} else if (state == RtpEndUserState.RINGING) {
|
||||||
}
|
setDialing();
|
||||||
|
} else if (state == RtpEndUserState.INCOMING_CALL) {
|
||||||
|
setRinging();
|
||||||
|
} else if (state == RtpEndUserState.CONNECTED) {
|
||||||
|
xmppConnectionService.setDiallerIntegrationActive(true);
|
||||||
|
setActive();
|
||||||
|
|
||||||
@Override
|
postDial();
|
||||||
public void onPostDialContinue(boolean c) {
|
} else if (state == RtpEndUserState.DECLINED_OR_BUSY) {
|
||||||
if (c) postDial();
|
setDisconnected(new DisconnectCause(DisconnectCause.BUSY));
|
||||||
}
|
} else if (state == RtpEndUserState.ENDED) {
|
||||||
|
setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
|
||||||
|
} else if (state == RtpEndUserState.RETRACTED) {
|
||||||
|
setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
|
||||||
|
} else if (RtpSessionActivity.END_CARD.contains(state)) {
|
||||||
|
setDisconnected(new DisconnectCause(DisconnectCause.ERROR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void sleep(int ms) {
|
@Override
|
||||||
try {
|
public void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set<AppRTCAudioManager.AudioDevice> availableAudioDevices) {
|
||||||
Thread.sleep(ms);
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O) return;
|
||||||
} catch (InterruptedException ex) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void postDial() {
|
switch (selectedAudioDevice) {
|
||||||
while (!postDial.empty()) {
|
case SPEAKER_PHONE:
|
||||||
String next = postDial.pop();
|
setAudioRoute(CallAudioState.ROUTE_SPEAKER);
|
||||||
if (next.equals(";")) {
|
case WIRED_HEADSET:
|
||||||
Stack v = (Stack) postDial.clone();
|
setAudioRoute(CallAudioState.ROUTE_WIRED_HEADSET);
|
||||||
Collections.reverse(v);
|
case EARPIECE:
|
||||||
setPostDialWait(String.join("", v));
|
setAudioRoute(CallAudioState.ROUTE_EARPIECE);
|
||||||
return;
|
case BLUETOOTH:
|
||||||
} else if (next.equals(",")) {
|
setAudioRoute(CallAudioState.ROUTE_BLUETOOTH);
|
||||||
sleep(2000);
|
default:
|
||||||
} else {
|
setAudioRoute(CallAudioState.ROUTE_WIRED_OR_EARPIECE);
|
||||||
rtpConnection.get().applyDtmfTone(next);
|
}
|
||||||
sleep(100);
|
}
|
||||||
}
|
|
||||||
}
|
@Override
|
||||||
}
|
public void onAnswer() {
|
||||||
}
|
// For incoming calls, a connection update may not have been triggered before answering
|
||||||
|
// so we have to acquire the rtp connection object here
|
||||||
|
this.rtpConnection =
|
||||||
|
xmppConnectionService.getJingleConnectionManager()
|
||||||
|
.findJingleRtpConnection(account, with, sessionId);
|
||||||
|
|
||||||
|
rtpConnection.get().acceptCall();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisconnect() {
|
||||||
|
if (rtpConnection == null || rtpConnection.get() == null) {
|
||||||
|
xmppConnectionService.getJingleConnectionManager().retractSessionProposal(account, with.asBareJid());
|
||||||
|
} else {
|
||||||
|
rtpConnection.get().endCall();
|
||||||
|
}
|
||||||
|
destroy();
|
||||||
|
xmppConnectionService.setDiallerIntegrationActive(false);
|
||||||
|
xmppConnectionService.removeRtpConnectionUpdateListener(
|
||||||
|
(XmppConnectionService.OnJingleRtpConnectionUpdate) this
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAbort() {
|
||||||
|
onDisconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayDtmfTone(char c) {
|
||||||
|
rtpConnection.get().applyDtmfTone("" + c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPostDialContinue(boolean c) {
|
||||||
|
if (c) postDial();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void sleep(int ms) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(ms);
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void postDial() {
|
||||||
|
while (!postDial.empty()) {
|
||||||
|
String next = postDial.pop();
|
||||||
|
if (next.equals(";")) {
|
||||||
|
Vector<String> v = new Vector<>(postDial);
|
||||||
|
Collections.reverse(v);
|
||||||
|
setPostDialWait(Joiner.on("").skipNulls().join(v));
|
||||||
|
return;
|
||||||
|
} else if (next.equals(",")) {
|
||||||
|
sleep(2000);
|
||||||
|
} else {
|
||||||
|
rtpConnection.get().applyDtmfTone(next);
|
||||||
|
sleep(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -599,7 +599,7 @@ public class Contact implements ListItem, Blockable {
|
||||||
"/" + getJid().asBareJid().toString();
|
"/" + getJid().asBareJid().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected PhoneAccountHandle phoneAccountHandle() {
|
public PhoneAccountHandle phoneAccountHandle() {
|
||||||
ComponentName componentName = new ComponentName(
|
ComponentName componentName = new ComponentName(
|
||||||
"com.cheogram.android",
|
"com.cheogram.android",
|
||||||
"com.cheogram.android.ConnectionService"
|
"com.cheogram.android.ConnectionService"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package eu.siacs.conversations.services;
|
package eu.siacs.conversations.services;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationChannelGroup;
|
import android.app.NotificationChannelGroup;
|
||||||
|
@ -8,6 +9,7 @@ import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
|
@ -16,9 +18,12 @@ import android.media.Ringtone;
|
||||||
import android.media.RingtoneManager;
|
import android.media.RingtoneManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.os.Vibrator;
|
import android.os.Vibrator;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.telecom.PhoneAccountHandle;
|
||||||
|
import android.telecom.TelecomManager;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
import android.text.style.StyleSpan;
|
import android.text.style.StyleSpan;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
|
@ -423,7 +428,58 @@ public class NotificationService {
|
||||||
notify(DELIVERY_FAILED_NOTIFICATION_ID, summaryNotification);
|
notify(DELIVERY_FAILED_NOTIFICATION_ID, summaryNotification);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private synchronized boolean tryRingingWithDialerUI(final AbstractJingleConnection.Id id, final Set<Media> media) {
|
||||||
|
if (mXmppConnectionService.checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// We cannot always request audio permission in Dialer UI
|
||||||
|
// e.g. when Dialer is shown over keyguard
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (media.size() != 1 || !media.contains(Media.AUDIO)) {
|
||||||
|
// Currently our ConnectionService only handles single audio calls
|
||||||
|
Log.w(Config.LOGTAG, "only audio calls can be handled by cheogram connection service");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PhoneAccountHandle handle = null;
|
||||||
|
for (Contact contact : id.account.getRoster().getContacts()) {
|
||||||
|
if (!contact.getJid().getDomain().equals(id.with.getDomain()))
|
||||||
|
continue;
|
||||||
|
if (!contact.getPresences().anyIdentity("gateway", "pstn"))
|
||||||
|
continue;
|
||||||
|
handle = contact.phoneAccountHandle();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle == null) {
|
||||||
|
Log.w(Config.LOGTAG, "Could not find phone account handle for " + id.account.getJid().toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bundle callInfo = new Bundle();
|
||||||
|
callInfo.putString("account", id.account.getJid().toString());
|
||||||
|
callInfo.putString("with", id.with.toString());
|
||||||
|
callInfo.putString("sessionId", id.sessionId);
|
||||||
|
|
||||||
|
TelecomManager telecomManager = mXmppConnectionService.getSystemService(TelecomManager.class);
|
||||||
|
|
||||||
|
try {
|
||||||
|
telecomManager.addNewIncomingCall(handle, callInfo);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
// There *could* be race conditions where the account is not registered yet
|
||||||
|
// when an incoming call is already received
|
||||||
|
Log.w(Config.LOGTAG, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void startRinging(final AbstractJingleConnection.Id id, final Set<Media> media) {
|
public synchronized void startRinging(final AbstractJingleConnection.Id id, final Set<Media> media) {
|
||||||
|
if (tryRingingWithDialerUI(id, media)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
showIncomingCallNotification(id, media);
|
showIncomingCallNotification(id, media);
|
||||||
final NotificationManager notificationManager = (NotificationManager) mXmppConnectionService.getSystemService(Context.NOTIFICATION_SERVICE);
|
final NotificationManager notificationManager = (NotificationManager) mXmppConnectionService.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
final int currentInterruptionFilter;
|
final int currentInterruptionFilter;
|
||||||
|
|
Loading…
Reference in a new issue