Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions app/jni/tgvoip/tgvoip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ struct TgCallsContext {
std::shared_ptr<tgcalls::VideoCaptureInterface> videoCapture;
std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> localVideoSink;
std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> remoteVideoSink;
bool videoCaptureAttached = false;
bool videoEnabled = false;
bool frontCamera = true;
};
Expand Down Expand Up @@ -633,6 +634,9 @@ JNI_OBJECT_FUNC(jlong, voip_TgCallsController, newInstance,
std::move(encryptionKey),
isOutgoingCall
),
// Direct video sessions must be created with a capture source attached so
// tgcalls can build the correct media graph. The source stays inactive
// until Java installs the EGL-backed sinks.
.videoCapture = isVideoCall ? videoCapture : nullptr,
.stateUpdated = [javaController](tgcalls::State state) {
javaController->runSafely([javaController, state](JNIEnv *env) {
Expand Down Expand Up @@ -689,7 +693,8 @@ JNI_OBJECT_FUNC(jlong, voip_TgCallsController, newInstance,
auto *context = new TgCallsContext;
context->javaController = javaController;
context->videoCapture = videoCapture;
context->videoEnabled = isVideoCall;
context->videoCaptureAttached = isVideoCall && videoCapture != nullptr;
context->videoEnabled = false;
context->tgcalls = tgcalls::Meta::Create(version, std::move(descriptor));
if (context->tgcalls == nullptr) {
delete context;
Expand All @@ -699,10 +704,6 @@ JNI_OBJECT_FUNC(jlong, voip_TgCallsController, newInstance,
context->tgcalls->setAudioOutputGainControlEnabled(audioOutputGainControlEnabled);
context->tgcalls->setEchoCancellationStrength(echoCancellationStrength);
context->tgcalls->setMuteMicrophone(muteMicrophone);
if (isVideoCall && context->videoCapture != nullptr) {
context->videoCapture->setState(tgcalls::VideoState::Active);
}

return jni::ptr_to_jlong(context);
}

Expand Down Expand Up @@ -797,10 +798,16 @@ JNI_OBJECT_FUNC(void, voip_TgCallsController, nativeSetVideoEnabled, jlong ptr,
}
context->videoEnabled = enabled;
if (enabled) {
if (!context->videoCaptureAttached) {
context->tgcalls->setVideoCapture(context->videoCapture);
context->videoCaptureAttached = true;
}
context->videoCapture->setState(tgcalls::VideoState::Active);
context->tgcalls->setVideoCapture(context->videoCapture);
} else {
context->tgcalls->setVideoCapture(nullptr);
if (context->videoCaptureAttached) {
context->tgcalls->setVideoCapture(nullptr);
context->videoCaptureAttached = false;
}
context->videoCapture->setState(tgcalls::VideoState::Inactive);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ public interface VideoStateListener {
void onVideoStateChanged (boolean supported, boolean localVideoEnabled, @VideoState int remoteVideoState, boolean frontCamera);
}

private static final String EXTRA_ANSWER_WITH_VIDEO = "answer_with_video";

@Override
public IBinder onBind (Intent intent) {
return null;
Expand Down Expand Up @@ -243,7 +245,7 @@ public void onReceive (Context context, Intent intent) {
}

if (Intents.ACTION_ANSWER_CALL.equals(action)) {
acceptIncomingCall();
acceptIncomingCall(intent.getBooleanExtra(EXTRA_ANSWER_WITH_VIDEO, true));
return;
}
}
Expand Down Expand Up @@ -724,9 +726,9 @@ public void onCallSettingsChanged (int callId, CallSettings settings) {

// Implementation

private void acceptIncomingCall () {
private void acceptIncomingCall (boolean withVideo) {
if (call != null) {
tdlib.context().calls().acceptCall(this, tdlib, call.id);
tdlib.context().calls().acceptCall(this, tdlib, call.id, withVideo);
if (UI.getUiState() != UI.State.RESUMED) {
bringCallToFront();
}
Expand Down Expand Up @@ -1016,7 +1018,7 @@ private boolean showIncomingNotification () {
}

builder
.setContentTitle(Lang.getString(R.string.CallBrandingIncoming))
.setContentTitle(Lang.getString(call != null && call.isVideo ? R.string.IncomingVideoCall : R.string.CallBrandingIncoming))
.setContentText(TD.getUserName(user))
.setSmallIcon(CALL_ICON_RES)
.setContentIntent(PendingIntent.getActivity(UI.getContext(), 0, Intents.valueOfCall(), PendingIntent.FLAG_ONE_SHOT | Intents.mutabilityFlags(false)));
Expand All @@ -1039,12 +1041,25 @@ private boolean showIncomingNotification () {
Intent answerIntent = new Intent();
Intents.secureIntent(answerIntent, false);
answerIntent.setAction(Intents.ACTION_ANSWER_CALL);
CharSequence answerTitle = Lang.getString(R.string.AnswerCall);
answerIntent.putExtra(EXTRA_ANSWER_WITH_VIDEO, true);
CharSequence answerTitle = Lang.getString(call != null && call.isVideo ? R.string.AnswerWithVideo : R.string.AnswerCall);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
answerTitle = new SpannableString(answerTitle);
((SpannableString) answerTitle).setSpan(new ForegroundColorSpan(Theme.getColor(ColorId.circleButtonPositive)), 0, answerTitle.length(), 0);
}
builder.addAction(R.drawable.round_call_24_white, answerTitle, PendingIntent.getBroadcast(this, 0, answerIntent, PendingIntent.FLAG_ONE_SHOT | Intents.mutabilityFlags(false)));
if (call != null && call.isVideo) {
Intent audioAnswerIntent = new Intent();
Intents.secureIntent(audioAnswerIntent, false);
audioAnswerIntent.setAction(Intents.ACTION_ANSWER_CALL);
audioAnswerIntent.putExtra(EXTRA_ANSWER_WITH_VIDEO, false);
CharSequence audioAnswerTitle = Lang.getString(R.string.AnswerWithoutVideo);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
audioAnswerTitle = new SpannableString(audioAnswerTitle);
((SpannableString) audioAnswerTitle).setSpan(new ForegroundColorSpan(Theme.getColor(ColorId.circleButtonPositive)), 0, audioAnswerTitle.length(), 0);
}
builder.addAction(R.drawable.round_call_24_white, audioAnswerTitle, PendingIntent.getBroadcast(this, 1, audioAnswerIntent, PendingIntent.FLAG_ONE_SHOT | Intents.mutabilityFlags(false)));
}
builder.addAction(R.drawable.round_call_24_white, answerTitle, PendingIntent.getBroadcast(this, 2, answerIntent, PendingIntent.FLAG_ONE_SHOT | Intents.mutabilityFlags(false)));
builder.setPriority(Notification.PRIORITY_MAX);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Expand Down Expand Up @@ -1509,7 +1524,8 @@ public void onRemoteMediaStateChanged (VoIPInstance context, @AudioState int aud
lastNetworkType,
audioGainControlEnabled,
echoCancellationStrength,
isMicDisabled
isMicDisabled,
tdlib.context().calls().shouldStartVideo(tdlib, call.id)
);
} catch (Throwable t) {
tgcallsTemp = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ public interface CurrentCallListener {
private Tdlib currentCallTdlib;
private @Nullable TdApi.Call currentCall;
private boolean currentCallAcknowledged;
private int answerWithoutVideoAccountId = TdlibAccount.NO_ID;
private int answerWithoutVideoCallId;

public void addCurrentCallListener (CurrentCallListener listener) {
listeners.add(listener);
Expand All @@ -83,6 +85,10 @@ private void setCurrentCall (final Tdlib tdlib, @Nullable final TdApi.Call call)
return;
}
if (currentCall == null || call == null) {
if (call == null) {
answerWithoutVideoAccountId = TdlibAccount.NO_ID;
answerWithoutVideoCallId = 0;
}
this.currentCallTdlib = tdlib;
this.currentCall = call;
this.currentCallAcknowledged = call == null || UI.getUiState() != UI.State.RESUMED || UI.isNavigationBusyWithSomething();
Expand Down Expand Up @@ -526,31 +532,48 @@ private boolean checkConnection (final Context context, final Tdlib tdlib) {
}

public void acceptCall (Context context, Tdlib tdlib, final int callId) {
acceptCall(context, tdlib, callId, true);
}

public void acceptCall (Context context, Tdlib tdlib, final int callId, final boolean withVideo) {
if (checkConnection(context, tdlib)) {
TdApi.Call pendingCall = tdlib.cache().getCall(callId);
if (!checkRecordPermissions(context, tdlib, pendingCall, 0, null)) {
return;
}
BaseActivity activity = UI.getUiContext();
if (pendingCall != null && pendingCall.isVideo && activity != null &&
if (withVideo && pendingCall != null && pendingCall.isVideo && activity != null &&
activity.permissions().requestAccessCameraPermission(granted -> {
if (granted) {
acceptCall(context, tdlib, callId);
acceptCall(context, tdlib, callId, true);
} else {
sendAcceptCall(tdlib, callId);
acceptCall(context, tdlib, callId, false);
}
})) {
return;
}
sendAcceptCall(tdlib, callId);
sendAcceptCall(tdlib, callId, withVideo);
}
}

private void sendAcceptCall (Tdlib tdlib, int callId) {
Log.v(Log.TAG_VOIP, "#%d: AcceptCall requested", callId);
private void sendAcceptCall (Tdlib tdlib, int callId, boolean withVideo) {
if (withVideo) {
if (answerWithoutVideoAccountId == tdlib.id() && answerWithoutVideoCallId == callId) {
answerWithoutVideoAccountId = TdlibAccount.NO_ID;
answerWithoutVideoCallId = 0;
}
} else {
answerWithoutVideoAccountId = tdlib.id();
answerWithoutVideoCallId = callId;
}
Log.v(Log.TAG_VOIP, "#%d: AcceptCall requested, withVideo:%b", callId, withVideo);
tdlib.client().send(new TdApi.AcceptCall(callId, VoIP.getProtocol()), object -> Log.v(Log.TAG_VOIP, "#%d: AcceptCall completed: %s", callId, object));
}

public boolean shouldStartVideo (Tdlib tdlib, int callId) {
return answerWithoutVideoAccountId != tdlib.id() || answerWithoutVideoCallId != callId;
}

public void hangUpCurrentCall () {
int currentCall = getCurrentCallId();
if (currentCall != 0) {
Expand Down
Loading
Loading