diff --git a/src/cheogram/AndroidManifest.xml b/src/cheogram/AndroidManifest.xml index 62396bed1af27fcdcac16db152115fbd362223ec..956e892fe1b1768858702f10c6c8aac4617bc18d 100644 --- a/src/cheogram/AndroidManifest.xml +++ b/src/cheogram/AndroidManifest.xml @@ -3,7 +3,18 @@ xmlns:tools="http://schemas.android.com/tools" package="eu.siacs.conversations"> + + + + + + + + + - - - - - - - - diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index 56e1ec02edcadbbd668ddab332764c5853ba33a0..555e35dceab3cdedc2bda5dfcb369d9609f45c0e 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -1,9 +1,14 @@ package eu.siacs.conversations.entities; +import android.content.ComponentName; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; +import android.os.Bundle; +import android.telecom.PhoneAccount; +import android.telecom.PhoneAccountHandle; +import android.telecom.TelecomManager; import android.text.TextUtils; import androidx.annotation.NonNull; @@ -559,6 +564,38 @@ public class Contact implements ListItem, Blockable { return changed; } + protected String phoneAccountLabel() { + return account.getJid().asBareJid().toString() + + "/" + getJid().asBareJid().toString(); + } + + protected PhoneAccountHandle phoneAccountHandle() { + ComponentName componentName = new ComponentName( + "com.cheogram.android", + "com.cheogram.android.ConnectionService" + ); + return new PhoneAccountHandle(componentName, phoneAccountLabel()); + } + + // This Contact is a gateway to use for voice calls, register it with OS + public void registerAsPhoneAccount(Context ctx) { + TelecomManager telecomManager = ctx.getSystemService(TelecomManager.class); + + PhoneAccount phoneAccount = PhoneAccount.builder( + phoneAccountHandle(), phoneAccountLabel() + ).setCapabilities( + PhoneAccount.CAPABILITY_CALL_PROVIDER + ).build(); + + telecomManager.registerPhoneAccount(phoneAccount); + } + + // Unregister any associated PSTN gateway integration + public void unregisterAsPhoneAccount(Context ctx) { + TelecomManager telecomManager = ctx.getSystemService(TelecomManager.class); + telecomManager.unregisterPhoneAccount(phoneAccountHandle()); + } + public static int getOption(Class clazz) { if (clazz == JabberIdContact.class) { return Options.SYNCED_VIA_ADDRESSBOOK; diff --git a/src/main/java/eu/siacs/conversations/entities/Presences.java b/src/main/java/eu/siacs/conversations/entities/Presences.java index 04d378cc2602e9fd47b1da60d9a9fdf3433114c6..4753e2138f7e4949a00d2180e2253f5434c2a78f 100644 --- a/src/main/java/eu/siacs/conversations/entities/Presences.java +++ b/src/main/java/eu/siacs/conversations/entities/Presences.java @@ -149,6 +149,22 @@ public class Presences { return false; } + public boolean anyIdentity(final String category, final String type) { + synchronized (this.presences) { + if (this.presences.size() == 0) { + // https://github.com/iNPUTmice/Conversations/issues/4230 + return false; + } + for (Presence presence : this.presences.values()) { + ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult(); + if (disco != null && disco.hasIdentity(category, type)) { + return true; + } + } + } + return false; + } + public Pair, Map> toTypeAndNameMap() { Map typeMap = new HashMap<>(); Map nameMap = new HashMap<>(); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 42b699e46e806cf95b0048f0fd320930a94cb967..c6e79d3af47ae6db8c5063a675c4dacc970befa5 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -233,6 +233,10 @@ public class XmppConnectionService extends Service { } } } + + if (contact.getPresences().anyIdentity("gateway", "pstn")) { + contact.registerAsPhoneAccount(this); + } }; private final PresenceGenerator mPresenceGenerator = new PresenceGenerator(this); private List accounts; @@ -1968,6 +1972,7 @@ public class XmppConnectionService extends Service { public void syncRoster(final Account account) { + unregisterPhoneAccounts(account); mRosterSyncTaskManager.execute(account, () -> databaseBackend.writeRoster(account.getRoster())); } @@ -3449,6 +3454,14 @@ public class XmppConnectionService extends Service { } } + protected void unregisterPhoneAccounts(final Account account) { + for (final Contact contact : account.getRoster().getContacts()) { + if (!contact.showInRoster()) { + contact.unregisterAsPhoneAccount(this); + } + } + } + public void createContact(final Contact contact, final boolean autoGrant) { createContact(contact, autoGrant, null); } diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index 8a2b87b47ed5d2499521b6fa367cca69d960a6f9..7a94829c3ac34da311c64f4cb24f7e3916fcd309 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -71,12 +71,18 @@ import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection; import eu.siacs.conversations.xmpp.jingle.Media; import eu.siacs.conversations.xmpp.jingle.RtpEndUserState; +import com.cheogram.android.ConnectionService.ConnectionBinder; + +import static eu.siacs.conversations.utils.PermissionUtils.getFirstDenied; +import static java.util.Arrays.asList; + public class RtpSessionActivity extends XmppActivity implements XmppConnectionService.OnJingleRtpConnectionUpdate, eu.siacs.conversations.ui.widget.SurfaceViewRenderer.OnAspectRatioChanged { public static final String EXTRA_WITH = "with"; public static final String EXTRA_SESSION_ID = "session_id"; public static final String EXTRA_LAST_REPORTED_STATE = "last_reported_state"; public static final String EXTRA_LAST_ACTION = "last_action"; + public static final String EXTRA_CONNECTION_BINDER = "connection_binder"; public static final String ACTION_ACCEPT_CALL = "action_accept_call"; public static final String ACTION_MAKE_VOICE_CALL = "action_make_voice_call"; public static final String ACTION_MAKE_VIDEO_CALL = "action_make_video_call"; @@ -142,6 +148,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe } } + protected android.os.IBinder connectionBinder = null; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -160,6 +168,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe boolean dialpadVisible = savedInstanceState.getBoolean("dialpad_visible"); binding.dialpad.setVisibility(dialpadVisible ? View.VISIBLE : View.GONE); } + + this.connectionBinder = getIntent().getExtras().getBinder(EXTRA_CONNECTION_BINDER); } @Override @@ -262,6 +272,29 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe } else { requireRtpConnection().endCall(); } + disconnectConnectionBinder(); + } + + private void disconnectConnectionBinder() { + if (connectionBinder != null) { + android.os.Parcel args = android.os.Parcel.obtain(); + try { + connectionBinder.transact(ConnectionBinder.TRANSACT_DISCONNECT, args, null, 0); + } catch (android.os.RemoteException e) {} + args.recycle(); + } + } + + private void activateConnectionBinder() { + // If we do this, the other UI takes over and kills our call + // So we can't activate that UI unless we are going to use it. + /*if (connectionBinder != null) { + android.os.Parcel args = android.os.Parcel.obtain(); + try { + connectionBinder.transact(ConnectionBinder.TRANSACT_ACTIVE, args, null, 0); + } catch (android.os.RemoteException e) {} + args.recycle(); + }*/ } private void retractSessionProposal() { @@ -1153,10 +1186,14 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe @Override public void onJingleRtpConnectionUpdate(Account account, Jid with, final String sessionId, RtpEndUserState state) { Log.d(Config.LOGTAG, "onJingleRtpConnectionUpdate(" + state + ")"); + if (state == RtpEndUserState.CONNECTED) { + activateConnectionBinder(); + } if (END_CARD.contains(state)) { Log.d(Config.LOGTAG, "end card reached"); releaseProximityWakeLock(); runOnUiThread(() -> getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)); + disconnectConnectionBinder(); } if (with.isBareJid()) { updateRtpSessionProposalState(account, with, state); diff --git a/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java b/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java index a3f062814ba47e26580a3d1dd0cee4bc62f56c58..5583fb5fbe1c304b25ea9957d70bd8f679af56a1 100644 --- a/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java @@ -260,8 +260,6 @@ public class UriHandlerActivity extends AppCompatActivity { break; case Intent.ACTION_VIEW: case Intent.ACTION_SENDTO: - case Intent.ACTION_DIAL: - case Intent.ACTION_CALL: if (handleUri(data.getData())) { finish(); } diff --git a/src/main/java/eu/siacs/conversations/utils/XmppUri.java b/src/main/java/eu/siacs/conversations/utils/XmppUri.java index 59f8cb822448297641d99443349fcf6d682693f3..6c3075be9b7ec1194d979d961c6bc3096aae76cf 100644 --- a/src/main/java/eu/siacs/conversations/utils/XmppUri.java +++ b/src/main/java/eu/siacs/conversations/utils/XmppUri.java @@ -179,8 +179,6 @@ public class XmppUri { } catch (final UnsupportedEncodingException ignored) { jid = null; } - } else if ("tel".equalsIgnoreCase(scheme)) { - jid = uri.getSchemeSpecificPart().replaceAll("[^\\d\\+]+", "") + "@cheogram.com"; } else { jid = null; }