From dd93de3833f1afb096424a0c4b0af8b235a1a056 Mon Sep 17 00:00:00 2001 From: adbenitez Date: Tue, 10 Jun 2025 15:20:34 +0200 Subject: [PATCH 01/12] improve the videocalls: open in internal webview --- src/main/AndroidManifest.xml | 7 ++ .../components/AttachmentTypeSelector.java | 4 -- .../securesms/connect/DcHelper.java | 9 ++- .../AdvancedPreferenceFragment.java | 5 +- .../videochat/VideochatActivity.java | 68 +++++++++++++++++++ .../securesms/videochat/VideochatUtil.java | 33 +++++++-- 6 files changed, 112 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/thoughtcrime/securesms/videochat/VideochatActivity.java diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 2473b9b9c4..11488fc126 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -367,6 +367,13 @@ android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"> + + + { + request.grant(request.getResources()); + }); + } + }); + + Util.runOnAnyBackgroundThread(() -> { + final DcChat chat = dcContext.getChat(chatId); + Util.runOnMain(() -> { + getSupportActionBar().setTitle(chat.getName()); + }); + }); + + webView.loadUrl(url); + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + // do not call super.onPrepareOptionsMenu() as the default "Search" menu is not needed + return true; + } + + @Override + protected boolean openOnlineUrl(String url) { + finish(); + return true; + } +} diff --git a/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java b/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java index dfd64f46bd..4bf8d4b8c4 100644 --- a/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java +++ b/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java @@ -1,6 +1,8 @@ package org.thoughtcrime.securesms.videochat; +import android.Manifest; import android.app.Activity; +import android.content.Intent; import androidx.appcompat.app.AlertDialog; @@ -10,7 +12,7 @@ import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.connect.DcHelper; -import org.thoughtcrime.securesms.util.IntentUtils; +import org.thoughtcrime.securesms.permissions.Permissions; public class VideochatUtil { @@ -23,7 +25,18 @@ public void invite(Activity activity, int chatId) { .setMessage(R.string.videochat_invite_user_hint) .setNegativeButton(R.string.cancel, null) .setPositiveButton(R.string.ok, (dialog, which) -> { + String instance = dcContext.getConfig(DcHelper.CONFIG_WEBRTC_INSTANCE); + boolean unset = instance == null || instance.isEmpty(); + if (unset) { + dcContext.setConfig(DcHelper.CONFIG_WEBRTC_INSTANCE, DcHelper.DEFAULT_VIDEOCHAT_URL); + } + int msgId = dcContext.sendVideochatInvitation(dcChat.getId()); + + if (unset) { + dcContext.setConfig(DcHelper.CONFIG_WEBRTC_INSTANCE, null); + } + if (msgId != 0) { join(activity, msgId); } @@ -32,10 +45,20 @@ public void invite(Activity activity, int chatId) { } public void join(Activity activity, int msgId) { - DcContext dcContext = DcHelper.getContext(activity); - DcMsg dcMsg = dcContext.getMsg(msgId); - String videochatUrl = dcMsg.getVideochatUrl(); - IntentUtils.showInBrowser(activity, videochatUrl); + Permissions.with(activity) + .request(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO) + .ifNecessary() + .withPermanentDenialDialog(activity.getString(R.string.perm_explain_access_to_camera_denied)) + .onAllGranted(() -> { + DcContext dcContext = DcHelper.getContext(activity); + DcMsg dcMsg = dcContext.getMsg(msgId); + Intent intent = new Intent(activity, VideochatActivity.class); + intent.setAction(Intent.ACTION_VIEW); + intent.putExtra(VideochatActivity.EXTRA_CHAT_ID, dcMsg.getChatId()); + intent.putExtra(VideochatActivity.EXTRA_URL, dcMsg.getVideochatUrl()); + activity.startActivity(intent); + }) + .execute(); } } From 5fb61b008b8d660dc42ed66c00053ae607975731 Mon Sep 17 00:00:00 2001 From: adbenitez Date: Tue, 10 Jun 2025 16:05:56 +0200 Subject: [PATCH 02/12] set name when joining call --- .../thoughtcrime/securesms/connect/DcHelper.java | 3 ++- .../securesms/videochat/VideochatUtil.java | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java b/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java index bfa9230ef3..b8f6d22c3c 100644 --- a/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java +++ b/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java @@ -74,7 +74,8 @@ public class DcHelper { public static final String CONFIG_WEBXDC_REALTIME_ENABLED = "webxdc_realtime_enabled"; public static final String CONFIG_PRIVATE_TAG = "private_tag"; - public static final String DEFAULT_VIDEOCHAT_URL = "https://meet.systemli.org/$ROOM#config.prejoinConfig.enabled=false&config.notifications=[]&config.toolbarButtons=[%22microphone%22,%22camera%22,%22hangup%22]"; + public static final String DEFAULT_VIDEOCHAT_URL_PREFIX = "https://meet.systemli.org/"; + public static final String DEFAULT_VIDEOCHAT_URL = DEFAULT_VIDEOCHAT_URL_PREFIX + "$ROOM#config.prejoinConfig.enabled=false&config.notifications=[]&config.toolbarButtons=[%22microphone%22,%22camera%22,%22hangup%22]"; public static DcContext getContext(@NonNull Context context) { return ApplicationContext.getInstance(context).dcContext; diff --git a/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java b/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java index 4bf8d4b8c4..8b0cd046a7 100644 --- a/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java +++ b/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java @@ -14,6 +14,10 @@ import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.permissions.Permissions; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + public class VideochatUtil { public void invite(Activity activity, int chatId) { @@ -52,10 +56,19 @@ public void join(Activity activity, int msgId) { .onAllGranted(() -> { DcContext dcContext = DcHelper.getContext(activity); DcMsg dcMsg = dcContext.getMsg(msgId); + String url = dcMsg.getVideochatUrl(); + if (url.startsWith(DcHelper.DEFAULT_VIDEOCHAT_URL_PREFIX) && url.contains("#")) { + String name = dcContext.getName(); + try { + name = URLEncoder.encode(dcContext.getName(), StandardCharsets.UTF_8.toString()); + } catch (UnsupportedEncodingException ignored) {} + url += "&userInfo.displayName=%22" + name +"%22"; + } + Intent intent = new Intent(activity, VideochatActivity.class); intent.setAction(Intent.ACTION_VIEW); intent.putExtra(VideochatActivity.EXTRA_CHAT_ID, dcMsg.getChatId()); - intent.putExtra(VideochatActivity.EXTRA_URL, dcMsg.getVideochatUrl()); + intent.putExtra(VideochatActivity.EXTRA_URL, url); activity.startActivity(intent); }) .execute(); From fa868077b0a0a350e49d0849f758a8753a64d22e Mon Sep 17 00:00:00 2001 From: adbenitez Date: Wed, 18 Jun 2025 17:41:09 +0200 Subject: [PATCH 03/12] test integrated webxdc --- .../thoughtcrime/securesms/WebxdcActivity.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/thoughtcrime/securesms/WebxdcActivity.java b/src/main/java/org/thoughtcrime/securesms/WebxdcActivity.java index 770ebbcbd6..7a125c62ae 100644 --- a/src/main/java/org/thoughtcrime/securesms/WebxdcActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/WebxdcActivity.java @@ -18,6 +18,7 @@ import android.view.View; import android.webkit.JavascriptInterface; import android.webkit.MimeTypeMap; +import android.webkit.PermissionRequest; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebResourceResponse; @@ -34,6 +35,7 @@ import androidx.core.graphics.drawable.IconCompat; import com.b44t.messenger.DcChat; +import com.b44t.messenger.DcContact; import com.b44t.messenger.DcContext; import com.b44t.messenger.DcEvent; import com.b44t.messenger.DcMsg; @@ -163,6 +165,13 @@ protected void onCreate(Bundle state, boolean ready) { setScreenMode(getResources().getConfiguration()); webView.setWebChromeClient(new WebChromeClient() { + @Override + public void onPermissionRequest(PermissionRequest request) { + Util.runOnMain(() -> { + request.grant(request.getResources()); + }); + } + @Override @RequiresApi(21) public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) { @@ -206,7 +215,7 @@ public boolean onShowFileChooser(WebView webView, ValueCallback filePathC this.baseURL = "https://acc" + dcContext.getAccountId() + "-msg" + appMessageId + ".localhost"; final JSONObject info = this.dcAppMsg.getWebxdcInfo(); - internetAccess = JsonUtils.optBoolean(info, "internet_access"); + internetAccess = true; //JsonUtils.optBoolean(info, "internet_access"); selfAddr = info.optString("self_addr"); sendUpdateMaxSize = info.optInt("send_update_max_size"); sendUpdateInterval = info.optInt("send_update_interval"); @@ -229,6 +238,10 @@ public boolean onShowFileChooser(WebView webView, ValueCallback filePathC String extraHref = b.getString(EXTRA_HREF, ""); if (TextUtils.isEmpty(extraHref)) { extraHref = "index.html"; + if (this.dcAppMsg.getFromId() == DcContact.DC_CONTACT_ID_SELF) { + extraHref += "#call"; + } + } String href = baseURL + "/" + extraHref; From eee218a3b659bc268f7f5ee6161f4e22d771789b Mon Sep 17 00:00:00 2001 From: "B. Petersen" Date: Thu, 19 Jun 2025 00:48:59 +0200 Subject: [PATCH 04/12] add call api --- jni/dc_wrapper.c | 24 +++++++++++++++++++ .../java/com/b44t/messenger/DcContext.java | 7 ++++++ src/main/java/com/b44t/messenger/DcMsg.java | 2 ++ 3 files changed, 33 insertions(+) diff --git a/jni/dc_wrapper.c b/jni/dc_wrapper.c index d21404fab5..9d9cd2cdc3 100644 --- a/jni/dc_wrapper.c +++ b/jni/dc_wrapper.c @@ -773,6 +773,30 @@ JNIEXPORT void Java_com_b44t_messenger_DcContext_setWebxdcIntegration(JNIEnv *en } +JNIEXPORT jint Java_com_b44t_messenger_DcContext_placeOutgoingCall(JNIEnv *env, jobject obj, jint chat_id, jstring info) +{ + CHAR_REF(info); + jint msg_id = dc_place_outgoing_call(get_dc_context(env, obj), chat_id, infoPtr); + CHAR_UNREF(info); + return msg_id; +} + + +JNIEXPORT jint Java_com_b44t_messenger_DcContext_acceptIncomingCall(JNIEnv *env, jobject obj, jint msg_id, jstring info) +{ + CHAR_REF(info); + jboolean ret = dc_accept_incoming_call(get_dc_context(env, obj), msg_id, infoPtr) != 0; + CHAR_UNREF(info); + return ret; +} + + +JNIEXPORT jint Java_com_b44t_messenger_DcContext_endCall(JNIEnv *env, jobject obj, jint msg_id) +{ + return dc_end_call(get_dc_context(env, obj), msg_id) != 0; +} + + JNIEXPORT jint Java_com_b44t_messenger_DcContext_addDeviceMsg(JNIEnv *env, jobject obj, jstring label, jobject msg) { CHAR_REF(label); diff --git a/src/main/java/com/b44t/messenger/DcContext.java b/src/main/java/com/b44t/messenger/DcContext.java index a428659afa..cec22f0aca 100644 --- a/src/main/java/com/b44t/messenger/DcContext.java +++ b/src/main/java/com/b44t/messenger/DcContext.java @@ -31,6 +31,10 @@ public class DcContext { public final static int DC_EVENT_WEBXDC_INSTANCE_DELETED = 2121; public final static int DC_EVENT_WEBXDC_REALTIME_DATA = 2150; public final static int DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE = 2200; + public final static int DC_EVENT_INCOMING_CALL = 2550; + public final static int DC_EVENT_INCOMING_CALL_ACCEPTED = 2560; + public final static int DC_EVENT_OUTGOING_CALL_ACCEPTED = 2570; + public final static int DC_EVENT_CALL_ENDED = 2580; public final static int DC_IMEX_EXPORT_SELF_KEYS = 1; public final static int DC_IMEX_IMPORT_SELF_KEYS = 2; @@ -203,6 +207,9 @@ protected void finalize() throws Throwable { public native String getWebxdcStatusUpdates(int msg_id, int last_known_serial); public native void setWebxdcIntegration (String file); public native int initWebxdcIntegration(int chat_id); + public native int placeOutgoingCall (int chat_id, String place_call_info); + public native boolean acceptIncomingCall (int msg_id, String accept_call_info); + public native boolean endCall (int msg_id); public native int addDeviceMsg (String label, DcMsg msg); public native boolean wasDeviceMsgEverAdded(String label); public DcLot checkQr (String qr) { return new DcLot(checkQrCPtr(qr)); } diff --git a/src/main/java/com/b44t/messenger/DcMsg.java b/src/main/java/com/b44t/messenger/DcMsg.java index dc7c700b87..4bae7fa2df 100644 --- a/src/main/java/com/b44t/messenger/DcMsg.java +++ b/src/main/java/com/b44t/messenger/DcMsg.java @@ -36,6 +36,8 @@ public class DcMsg { public final static int DC_INFO_PROTECTION_DISABLED = 12; public final static int DC_INFO_INVALID_UNENCRYPTED_MAIL = 13; public final static int DC_INFO_WEBXDC_INFO_MESSAGE = 32; + public final static int DC_INFO_OUTGOING_CALL = 50; + public final static int DC_INFO_INCOMING_CALL = 55; public final static int DC_STATE_UNDEFINED = 0; public final static int DC_STATE_IN_FRESH = 10; From 3086c723046e804f529e45aaa427890acc6e7acc Mon Sep 17 00:00:00 2001 From: adbenitez Date: Thu, 19 Jun 2025 17:17:53 +0200 Subject: [PATCH 05/12] undo temporary test --- .../securesms/ConversationActivity.java | 7 ++- .../securesms/ConversationFragment.java | 2 +- .../securesms/WebxdcActivity.java | 15 +----- .../videochat/VideochatActivity.java | 10 ++-- .../securesms/videochat/VideochatUtil.java | 54 ++++++++++--------- 5 files changed, 41 insertions(+), 47 deletions(-) diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java b/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java index cdd31e0a87..d090f82846 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java @@ -951,7 +951,12 @@ private void addAttachment(int type) { case AttachmentTypeSelector.ADD_DOCUMENT: AttachmentManager.selectDocument(this, PICK_DOCUMENT); break; case AttachmentTypeSelector.INVITE_VIDEO_CHAT: - new VideochatUtil().invite(this, chatId); break; + if (isMultiUser()) { + VideochatUtil.startMeeting(this, chatId); + } else { + VideochatUtil.startCall(this, chatId); + } + break; case AttachmentTypeSelector.ADD_CONTACT_INFO: startContactChooserActivity(); break; case AttachmentTypeSelector.ADD_LOCATION: diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationFragment.java b/src/main/java/org/thoughtcrime/securesms/ConversationFragment.java index 5ab8be4969..e6daec3b36 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationFragment.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationFragment.java @@ -741,7 +741,7 @@ public void onItemClick(DcMsg messageRecord) { } } else if (messageRecord.getType()==DcMsg.DC_MSG_VIDEOCHAT_INVITATION) { - new VideochatUtil().join(getActivity(), messageRecord.getId()); + VideochatUtil.joinMeeting(getActivity(), messageRecord.getId()); } else if(DozeReminder.isDozeReminderMsg(getContext(), messageRecord)) { DozeReminder.dozeReminderTapped(getContext()); diff --git a/src/main/java/org/thoughtcrime/securesms/WebxdcActivity.java b/src/main/java/org/thoughtcrime/securesms/WebxdcActivity.java index 7a125c62ae..770ebbcbd6 100644 --- a/src/main/java/org/thoughtcrime/securesms/WebxdcActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/WebxdcActivity.java @@ -18,7 +18,6 @@ import android.view.View; import android.webkit.JavascriptInterface; import android.webkit.MimeTypeMap; -import android.webkit.PermissionRequest; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebResourceResponse; @@ -35,7 +34,6 @@ import androidx.core.graphics.drawable.IconCompat; import com.b44t.messenger.DcChat; -import com.b44t.messenger.DcContact; import com.b44t.messenger.DcContext; import com.b44t.messenger.DcEvent; import com.b44t.messenger.DcMsg; @@ -165,13 +163,6 @@ protected void onCreate(Bundle state, boolean ready) { setScreenMode(getResources().getConfiguration()); webView.setWebChromeClient(new WebChromeClient() { - @Override - public void onPermissionRequest(PermissionRequest request) { - Util.runOnMain(() -> { - request.grant(request.getResources()); - }); - } - @Override @RequiresApi(21) public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) { @@ -215,7 +206,7 @@ public boolean onShowFileChooser(WebView webView, ValueCallback filePathC this.baseURL = "https://acc" + dcContext.getAccountId() + "-msg" + appMessageId + ".localhost"; final JSONObject info = this.dcAppMsg.getWebxdcInfo(); - internetAccess = true; //JsonUtils.optBoolean(info, "internet_access"); + internetAccess = JsonUtils.optBoolean(info, "internet_access"); selfAddr = info.optString("self_addr"); sendUpdateMaxSize = info.optInt("send_update_max_size"); sendUpdateInterval = info.optInt("send_update_interval"); @@ -238,10 +229,6 @@ public boolean onShowFileChooser(WebView webView, ValueCallback filePathC String extraHref = b.getString(EXTRA_HREF, ""); if (TextUtils.isEmpty(extraHref)) { extraHref = "index.html"; - if (this.dcAppMsg.getFromId() == DcContact.DC_CONTACT_ID_SELF) { - extraHref += "#call"; - } - } String href = baseURL + "/" + extraHref; diff --git a/src/main/java/org/thoughtcrime/securesms/videochat/VideochatActivity.java b/src/main/java/org/thoughtcrime/securesms/videochat/VideochatActivity.java index ca80ffd97a..5997e4cd06 100644 --- a/src/main/java/org/thoughtcrime/securesms/videochat/VideochatActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/videochat/VideochatActivity.java @@ -17,10 +17,10 @@ public class VideochatActivity extends WebViewActivity { private static final String TAG = VideochatActivity.class.getSimpleName(); public static final String EXTRA_CHAT_ID = "chat_id"; - public static final String EXTRA_URL = "url"; + public static final String EXTRA_HASH = "hash"; private DcContext dcContext; - private String url = ""; + private String url = "file:///android_asset/call.html"; @Override protected void onCreate(Bundle state, boolean ready) { @@ -28,12 +28,12 @@ protected void onCreate(Bundle state, boolean ready) { this.dcContext = DcHelper.getContext(getApplicationContext()); Bundle b = getIntent().getExtras(); - url = b.getString(EXTRA_URL, ""); + String hash = b.getString(EXTRA_HASH, ""); int chatId = b.getInt(EXTRA_CHAT_ID, 0); WebSettings webSettings = webView.getSettings(); webSettings.setJavaScriptEnabled(true); - webSettings.setUserAgentString("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"); + webSettings.setMediaPlaybackRequiresUserGesture(false); webView.setWebChromeClient(new WebChromeClient() { @Override @@ -51,7 +51,7 @@ public void onPermissionRequest(PermissionRequest request) { }); }); - webView.loadUrl(url); + webView.loadUrl(url+hash); } @Override diff --git a/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java b/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java index 8b0cd046a7..e795ed80dc 100644 --- a/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java +++ b/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java @@ -20,7 +20,7 @@ public class VideochatUtil { - public void invite(Activity activity, int chatId) { + public static void startMeeting(Activity activity, int chatId) { DcContext dcContext = DcHelper.getContext(activity); DcChat dcChat = dcContext.getChat(chatId); @@ -29,18 +29,7 @@ public void invite(Activity activity, int chatId) { .setMessage(R.string.videochat_invite_user_hint) .setNegativeButton(R.string.cancel, null) .setPositiveButton(R.string.ok, (dialog, which) -> { - String instance = dcContext.getConfig(DcHelper.CONFIG_WEBRTC_INSTANCE); - boolean unset = instance == null || instance.isEmpty(); - if (unset) { - dcContext.setConfig(DcHelper.CONFIG_WEBRTC_INSTANCE, DcHelper.DEFAULT_VIDEOCHAT_URL); - } - int msgId = dcContext.sendVideochatInvitation(dcChat.getId()); - - if (unset) { - dcContext.setConfig(DcHelper.CONFIG_WEBRTC_INSTANCE, null); - } - if (msgId != 0) { join(activity, msgId); } @@ -48,27 +37,40 @@ public void invite(Activity activity, int chatId) { .show(); } - public void join(Activity activity, int msgId) { + public static void joinMeeting(Activity activity, int msgId) { + DcContext dcContext = DcHelper.getContext(activity); + DcMsg dcMsg = dcContext.getMsg(msgId); + String videochatUrl = dcMsg.getVideochatUrl(); + IntentUtils.showInBrowser(activity, videochatUrl); + } + + public static void startCall(Activity activity, int chatId) { + DcContext dcContext = DcHelper.getContext(activity); + DcChat dcChat = dcContext.getChat(chatId); + + new AlertDialog.Builder(activity) + .setTitle(activity.getString(R.string.videochat_invite_user_to_videochat, dcChat.getName())) + .setNegativeButton(R.string.cancel, null) + .setPositiveButton(R.string.ok, (dialog, which) -> { + joinCall(activity, chatId, "#call"); + }) + .show(); + } + + public static void joinCall(Activity activity, int chatId) { + joinCall(activity, chatId, ""); + } + + private static void joinCall(Activity activity, int chatId, String hash) { Permissions.with(activity) .request(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO) .ifNecessary() .withPermanentDenialDialog(activity.getString(R.string.perm_explain_access_to_camera_denied)) .onAllGranted(() -> { - DcContext dcContext = DcHelper.getContext(activity); - DcMsg dcMsg = dcContext.getMsg(msgId); - String url = dcMsg.getVideochatUrl(); - if (url.startsWith(DcHelper.DEFAULT_VIDEOCHAT_URL_PREFIX) && url.contains("#")) { - String name = dcContext.getName(); - try { - name = URLEncoder.encode(dcContext.getName(), StandardCharsets.UTF_8.toString()); - } catch (UnsupportedEncodingException ignored) {} - url += "&userInfo.displayName=%22" + name +"%22"; - } - Intent intent = new Intent(activity, VideochatActivity.class); intent.setAction(Intent.ACTION_VIEW); - intent.putExtra(VideochatActivity.EXTRA_CHAT_ID, dcMsg.getChatId()); - intent.putExtra(VideochatActivity.EXTRA_URL, url); + intent.putExtra(VideochatActivity.EXTRA_CHAT_ID, chatId); + intent.putExtra(VideochatActivity.EXTRA_HASH, hash); activity.startActivity(intent); }) .execute(); From 27f662707a74d98ee7fff87d4554b1ea8aed8efd Mon Sep 17 00:00:00 2001 From: adbenitez Date: Thu, 19 Jun 2025 17:20:46 +0200 Subject: [PATCH 06/12] fix VideochatUtil --- .../org/thoughtcrime/securesms/videochat/VideochatUtil.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java b/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java index e795ed80dc..716faf0c48 100644 --- a/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java +++ b/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java @@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.util.IntentUtils; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; @@ -31,7 +32,7 @@ public static void startMeeting(Activity activity, int chatId) { .setPositiveButton(R.string.ok, (dialog, which) -> { int msgId = dcContext.sendVideochatInvitation(dcChat.getId()); if (msgId != 0) { - join(activity, msgId); + joinMeeting(activity, msgId); } }) .show(); From 365f1dbbe6dc609bf8dbdd2c82538327bb37c8bc Mon Sep 17 00:00:00 2001 From: adbenitez Date: Fri, 20 Jun 2025 18:56:45 +0200 Subject: [PATCH 07/12] basic call --- jni/deltachat-core-rust | 2 +- src/main/assets/call.html | 72 ++++++++++++++++++ .../securesms/ConversationListFragment.java | 5 ++ .../videochat/VideochatActivity.java | 75 +++++++++++++++---- .../securesms/videochat/VideochatUtil.java | 16 ++-- 5 files changed, 148 insertions(+), 22 deletions(-) create mode 100644 src/main/assets/call.html diff --git a/jni/deltachat-core-rust b/jni/deltachat-core-rust index 47b9bfc8bf..c1e2f0bb18 160000 --- a/jni/deltachat-core-rust +++ b/jni/deltachat-core-rust @@ -1 +1 @@ -Subproject commit 47b9bfc8bf807ad099d2211a59d6ba80290b0c95 +Subproject commit c1e2f0bb18bfadaaf0d78848b0e990ad3b1538c7 diff --git a/src/main/assets/call.html b/src/main/assets/call.html new file mode 100644 index 0000000000..15da98d8ca --- /dev/null +++ b/src/main/assets/call.html @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + +
+ + diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationListFragment.java b/src/main/java/org/thoughtcrime/securesms/ConversationListFragment.java index ea928ec745..af81a13b6f 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationListFragment.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationListFragment.java @@ -51,6 +51,7 @@ import org.thoughtcrime.securesms.util.RelayUtil; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; +import org.thoughtcrime.securesms.videochat.VideochatUtil; import java.util.Timer; import java.util.TimerTask; @@ -81,6 +82,7 @@ public void onCreate(Bundle icicle) { eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_INCOMING_MSG, this); eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_MSGS_NOTICED, this); eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_CHAT_DELETED, this); + eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_INCOMING_CALL, this); eventCenter.addObserver(DcContext.DC_EVENT_CHAT_MODIFIED, this); eventCenter.addObserver(DcContext.DC_EVENT_CONTACTS_CHANGED, this); eventCenter.addObserver(DcContext.DC_EVENT_MSGS_CHANGED, this); @@ -343,6 +345,9 @@ public void handleEvent(@NonNull DcEvent event) { ((ConversationListActivity) activity).refreshAvatar(); } + } else if (event.getId() == DcContext.DC_EVENT_INCOMING_CALL) { + // TODO: show notification + VideochatUtil.joinCall(getActivity(), event.getData1Int(), event.getData2Str()); } else { loadChatlistAsync(); } diff --git a/src/main/java/org/thoughtcrime/securesms/videochat/VideochatActivity.java b/src/main/java/org/thoughtcrime/securesms/videochat/VideochatActivity.java index 5997e4cd06..7833b63d42 100644 --- a/src/main/java/org/thoughtcrime/securesms/videochat/VideochatActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/videochat/VideochatActivity.java @@ -1,57 +1,71 @@ package org.thoughtcrime.securesms.videochat; +import android.annotation.SuppressLint; import android.os.Bundle; import android.view.Menu; +import android.webkit.JavascriptInterface; import android.webkit.PermissionRequest; import android.webkit.WebChromeClient; import android.webkit.WebSettings; +import androidx.annotation.NonNull; + import com.b44t.messenger.DcChat; import com.b44t.messenger.DcContext; +import com.b44t.messenger.DcEvent; import org.thoughtcrime.securesms.WebViewActivity; +import org.thoughtcrime.securesms.connect.DcEventCenter; import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.util.Util; -public class VideochatActivity extends WebViewActivity { - private static final String TAG = VideochatActivity.class.getSimpleName(); +import java.util.Objects; + +public class VideochatActivity extends WebViewActivity implements DcEventCenter.DcEventDelegate { public static final String EXTRA_CHAT_ID = "chat_id"; + public static final String EXTRA_CALL_ID = "call_id"; public static final String EXTRA_HASH = "hash"; private DcContext dcContext; - private String url = "file:///android_asset/call.html"; + private int chatId; + private int callId; + @SuppressLint("SetJavaScriptEnabled") @Override protected void onCreate(Bundle state, boolean ready) { super.onCreate(state, ready); this.dcContext = DcHelper.getContext(getApplicationContext()); - Bundle b = getIntent().getExtras(); - String hash = b.getString(EXTRA_HASH, ""); - int chatId = b.getInt(EXTRA_CHAT_ID, 0); + Bundle bundle = getIntent().getExtras(); + assert bundle != null; + String hash = bundle.getString(EXTRA_HASH, ""); + chatId = bundle.getInt(EXTRA_CHAT_ID, 0); + callId = bundle.getInt(EXTRA_CALL_ID, 0); WebSettings webSettings = webView.getSettings(); webSettings.setJavaScriptEnabled(true); webSettings.setMediaPlaybackRequiresUserGesture(false); + webView.addJavascriptInterface(new InternalJSApi(), "calls"); webView.setWebChromeClient(new WebChromeClient() { @Override public void onPermissionRequest(PermissionRequest request) { - Util.runOnMain(() -> { - request.grant(request.getResources()); - }); + Util.runOnMain(() -> request.grant(request.getResources())); } }); + DcEventCenter eventCenter = DcHelper.getEventCenter(getApplicationContext()); + eventCenter.addObserver(DcContext.DC_EVENT_OUTGOING_CALL_ACCEPTED, this); + eventCenter.addObserver(DcContext.DC_EVENT_CALL_ENDED, this); + Util.runOnAnyBackgroundThread(() -> { final DcChat chat = dcContext.getChat(chatId); - Util.runOnMain(() -> { - getSupportActionBar().setTitle(chat.getName()); - }); + Util.runOnMain(() -> Objects.requireNonNull(getSupportActionBar()).setTitle(chat.getName())); }); - webView.loadUrl(url+hash); + String url = "file:///android_asset/call.html"; + webView.loadUrl(url + hash); } @Override @@ -65,4 +79,39 @@ protected boolean openOnlineUrl(String url) { finish(); return true; } + + @Override + public void handleEvent(@NonNull DcEvent event) { + switch (event.getId()) { + case DcContext.DC_EVENT_OUTGOING_CALL_ACCEPTED: + if (event.getData1Int() == callId) { + String hash = "#answer=" + event.getData2Str(); + webView.evaluateJavascript("window.location.hash = `"+hash+"`", null); + } + break; + case DcContext.DC_EVENT_CALL_ENDED: + if (event.getData1Int() == callId) { + finish(); + } + break; + } + } + + + class InternalJSApi { + @JavascriptInterface + public void startCall(String payload) { + callId = dcContext.placeOutgoingCall(chatId, payload); + } + + @JavascriptInterface + public void acceptCall(String payload) { + dcContext.acceptIncomingCall(callId, payload); + } + + @JavascriptInterface + public void endCall() { + dcContext.endCall(callId); + } + } } diff --git a/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java b/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java index 716faf0c48..00778f577b 100644 --- a/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java +++ b/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java @@ -15,10 +15,6 @@ import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.util.IntentUtils; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; - public class VideochatUtil { public static void startMeeting(Activity activity, int chatId) { @@ -53,16 +49,19 @@ public static void startCall(Activity activity, int chatId) { .setTitle(activity.getString(R.string.videochat_invite_user_to_videochat, dcChat.getName())) .setNegativeButton(R.string.cancel, null) .setPositiveButton(R.string.ok, (dialog, which) -> { - joinCall(activity, chatId, "#call"); + openCall(activity, chatId, 0, "#call"); }) .show(); } - public static void joinCall(Activity activity, int chatId) { - joinCall(activity, chatId, ""); + public static void joinCall(Activity activity, int callId, String payload) { + DcContext dcContext = DcHelper.getContext(activity); + DcMsg dcMsg = dcContext.getMsg(callId); + String hash = "#offer=" + payload; + openCall(activity, dcMsg.getChatId(), callId, hash); } - private static void joinCall(Activity activity, int chatId, String hash) { + private static void openCall(Activity activity, int chatId, int callId, String hash) { Permissions.with(activity) .request(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO) .ifNecessary() @@ -71,6 +70,7 @@ private static void joinCall(Activity activity, int chatId, String hash) { Intent intent = new Intent(activity, VideochatActivity.class); intent.setAction(Intent.ACTION_VIEW); intent.putExtra(VideochatActivity.EXTRA_CHAT_ID, chatId); + intent.putExtra(VideochatActivity.EXTRA_CALL_ID, callId); intent.putExtra(VideochatActivity.EXTRA_HASH, hash); activity.startActivity(intent); }) From 571f49ab13095e3bc447fa8c168ff6c9f05df400 Mon Sep 17 00:00:00 2001 From: adbenitez Date: Fri, 20 Jun 2025 19:04:42 +0200 Subject: [PATCH 08/12] revert meeting change --- .../securesms/components/AttachmentTypeSelector.java | 4 ++++ .../org/thoughtcrime/securesms/connect/DcHelper.java | 10 ++-------- .../preferences/AdvancedPreferenceFragment.java | 5 +++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java b/src/main/java/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java index 87e65e27b9..5526d9b569 100644 --- a/src/main/java/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java +++ b/src/main/java/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java @@ -92,6 +92,10 @@ public AttachmentTypeSelector(@NonNull Context context, @NonNull LoaderManager l ViewUtil.findById(layout, R.id.location_linear_layout).setVisibility(View.GONE); } + if (!DcHelper.isWebrtcConfigOk(DcHelper.getContext(context))) { + ViewUtil.findById(layout, R.id.invite_video_chat_linear_layout).setVisibility(View.GONE); + } + setLocationButtonImage(context); setContentView(layout); diff --git a/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java b/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java index b8f6d22c3c..f4dec06d1f 100644 --- a/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java +++ b/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java @@ -74,9 +74,6 @@ public class DcHelper { public static final String CONFIG_WEBXDC_REALTIME_ENABLED = "webxdc_realtime_enabled"; public static final String CONFIG_PRIVATE_TAG = "private_tag"; - public static final String DEFAULT_VIDEOCHAT_URL_PREFIX = "https://meet.systemli.org/"; - public static final String DEFAULT_VIDEOCHAT_URL = DEFAULT_VIDEOCHAT_URL_PREFIX + "$ROOM#config.prejoinConfig.enabled=false&config.notifications=[]&config.toolbarButtons=[%22microphone%22,%22camera%22,%22hangup%22]"; - public static DcContext getContext(@NonNull Context context) { return ApplicationContext.getInstance(context).dcContext; } @@ -406,12 +403,9 @@ public static String getBlobdirFile(DcContext dcContext, String path) { } - public static String getVideochatURL(DcContext dcContext) { + public static boolean isWebrtcConfigOk(DcContext dcContext) { String instance = dcContext.getConfig(DcHelper.CONFIG_WEBRTC_INSTANCE); - if (instance != null && !instance.isEmpty()) { - return instance; - } - return DEFAULT_VIDEOCHAT_URL; + return (instance != null && !instance.isEmpty()); } @NonNull diff --git a/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java b/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java index a7b24c8f88..a41f302ed6 100644 --- a/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java +++ b/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java @@ -338,7 +338,7 @@ public boolean onPreferenceClick(@NonNull Preference preference) { View gl = View.inflate(requireActivity(), R.layout.single_line_input, null); EditText inputField = gl.findViewById(R.id.input_field); inputField.setHint(R.string.videochat_instance_placeholder); - inputField.setText(DcHelper.getVideochatURL(dcContext)); + inputField.setText(dcContext.getConfig(DcHelper.CONFIG_WEBRTC_INSTANCE)); inputField.setSelection(inputField.getText().length()); inputField.setInputType(TYPE_TEXT_VARIATION_URI); new AlertDialog.Builder(requireActivity()) @@ -381,7 +381,8 @@ public boolean onPreferenceClick(@NonNull Preference preference) { private void updateWebrtcSummary() { Preference webrtcInstance = this.findPreference("pref_webrtc_instance"); if (webrtcInstance != null) { - webrtcInstance.setSummary(DcHelper.getVideochatURL(dcContext)); + webrtcInstance.setSummary(DcHelper.isWebrtcConfigOk(dcContext)? + dcContext.getConfig(DcHelper.CONFIG_WEBRTC_INSTANCE) : getString(R.string.none)); } } From 8ab9b92517a8f4a78375129bbf97f492ff1fc65c Mon Sep 17 00:00:00 2001 From: adbenitez Date: Mon, 18 Aug 2025 14:47:57 +0200 Subject: [PATCH 09/12] update core --- jni/deltachat-core-rust | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jni/deltachat-core-rust b/jni/deltachat-core-rust index c1e2f0bb18..2e2cfc4cb3 160000 --- a/jni/deltachat-core-rust +++ b/jni/deltachat-core-rust @@ -1 +1 @@ -Subproject commit c1e2f0bb18bfadaaf0d78848b0e990ad3b1538c7 +Subproject commit 2e2cfc4cb3cee10b9a92489003f5b5a0b924d1e6 From 7cc9d7ea2f1cff6d9b3b5cc712ea439385ed2215 Mon Sep 17 00:00:00 2001 From: adbenitez Date: Mon, 18 Aug 2025 14:58:35 +0200 Subject: [PATCH 10/12] update core --- jni/deltachat-core-rust | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jni/deltachat-core-rust b/jni/deltachat-core-rust index 2e2cfc4cb3..fa3616a7cb 160000 --- a/jni/deltachat-core-rust +++ b/jni/deltachat-core-rust @@ -1 +1 @@ -Subproject commit 2e2cfc4cb3cee10b9a92489003f5b5a0b924d1e6 +Subproject commit fa3616a7cb26b81ebb65d2dd6624f3c2bdc548aa From c9ac4213159d780f8538217c206e4a0a3681b2fa Mon Sep 17 00:00:00 2001 From: adbenitez Date: Tue, 19 Aug 2025 14:00:51 +0200 Subject: [PATCH 11/12] update call.html --- src/main/assets/call.html | 56 +-------------------------------------- 1 file changed, 1 insertion(+), 55 deletions(-) diff --git a/src/main/assets/call.html b/src/main/assets/call.html index 15da98d8ca..f0de1f9d8d 100644 --- a/src/main/assets/call.html +++ b/src/main/assets/call.html @@ -9,62 +9,8 @@ - + - -
From 1c7c3f06e60038ea06081c08a3e3175de00c8ebd Mon Sep 17 00:00:00 2001 From: adbenitez Date: Fri, 22 Aug 2025 15:17:32 +0200 Subject: [PATCH 12/12] prototype with TelecomManager and ConnectionService APIs --- src/main/AndroidManifest.xml | 10 ++ .../securesms/ConversationListFragment.java | 1 - .../videochat/CallIntegrationService.java | 109 ++++++++++++++++++ .../securesms/videochat/VideochatUtil.java | 13 +++ 4 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/thoughtcrime/securesms/videochat/CallIntegrationService.java diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 03c7426cd2..b987209ecc 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -40,6 +40,7 @@ + @@ -400,6 +401,15 @@ android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
+ + + + + + = Build.VERSION_CODES.TIRAMISU) { + if (telecomManager.getOwnSelfManagedPhoneAccounts().contains(handle)) { + Log.d(TAG, "a phone account for " + accountId + " already exists"); + return; + } + } + final PhoneAccount.Builder builder = + PhoneAccount.builder(getHandle(context, accountId), accountId+""); + builder.setSupportedUriSchemes(Collections.singletonList("dc")); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + builder.setCapabilities( + PhoneAccount.CAPABILITY_SELF_MANAGED + | PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING); + } + final PhoneAccount phoneAccount = builder.build(); + telecomManager.registerPhoneAccount(phoneAccount); + } + + public static void placeCall(Context context, int accountId, int chatId) { + final Bundle extras = new Bundle(); + extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, getHandle(context, accountId)); + final Bundle outgoingCallExtras = new Bundle(); + outgoingCallExtras.putInt(EXTRA_ACCOUNT_ID, accountId); + outgoingCallExtras.putInt(EXTRA_CHAT_ID, chatId); + extras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, outgoingCallExtras); + + final Uri address = Uri.parse("tel:0"); + try { + context.getSystemService(TelecomManager.class).placeCall(address, extras); + } catch (final SecurityException e) { + Log.e(TAG, "call integration not available", e); + Toast.makeText(context, "call integration not available", Toast.LENGTH_LONG).show(); + } + } + + public static void addNewIncomingCall(final Context context, int accId, int chatId, int callId, String payload) { + final PhoneAccountHandle phoneAccountHandle = getHandle(context, accId); + final Bundle bundle = new Bundle(); + bundle.putString(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, chatId+""); + final Bundle extras = new Bundle(); + extras.putInt(EXTRA_CALL_ID, callId); + extras.putString(EXTRA_CALL_PAYLOAD, payload); + bundle.putBundle(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras); + + try { + context.getSystemService(TelecomManager.class).addNewIncomingCall(phoneAccountHandle, bundle); + } catch (final SecurityException e) { + Log.e(TAG, "call integration not available", e); + Toast.makeText(context, "call integration not available", Toast.LENGTH_LONG).show(); + } + } + +} diff --git a/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java b/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java index 00778f577b..1c1784eb33 100644 --- a/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java +++ b/src/main/java/org/thoughtcrime/securesms/videochat/VideochatUtil.java @@ -3,6 +3,7 @@ import android.Manifest; import android.app.Activity; import android.content.Intent; +import android.os.Build; import androidx.appcompat.app.AlertDialog; @@ -57,16 +58,28 @@ public static void startCall(Activity activity, int chatId) { public static void joinCall(Activity activity, int callId, String payload) { DcContext dcContext = DcHelper.getContext(activity); DcMsg dcMsg = dcContext.getMsg(callId); + int accId = dcContext.getAccountId(); + int chatId = dcMsg.getChatId(); String hash = "#offer=" + payload; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + CallIntegrationService.addNewIncomingCall(activity, accId, chatId, callId, payload); + } openCall(activity, dcMsg.getChatId(), callId, hash); } private static void openCall(Activity activity, int chatId, int callId, String hash) { + DcContext dcContext = DcHelper.getContext(activity); + int accId = dcContext.getAccountId(); + Permissions.with(activity) .request(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO) .ifNecessary() .withPermanentDenialDialog(activity.getString(R.string.perm_explain_access_to_camera_denied)) .onAllGranted(() -> { + if ("#call".equals(hash) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + CallIntegrationService.registerPhoneAccount(activity, accId); + CallIntegrationService.placeCall(activity, accId, chatId); + } Intent intent = new Intent(activity, VideochatActivity.class); intent.setAction(Intent.ACTION_VIEW); intent.putExtra(VideochatActivity.EXTRA_CHAT_ID, chatId);