Add shared calling intent system.

This commit is contained in:
Alex Hart 2024-09-03 15:22:44 -03:00 committed by Cody Henthorne
parent e5b482c7ad
commit 4d23f11f6e
12 changed files with 277 additions and 90 deletions

View file

@ -84,9 +84,10 @@ import org.thoughtcrime.securesms.components.webrtc.controls.ControlsAndInfoCont
import org.thoughtcrime.securesms.components.webrtc.controls.ControlsAndInfoViewModel; import org.thoughtcrime.securesms.components.webrtc.controls.ControlsAndInfoViewModel;
import org.thoughtcrime.securesms.components.webrtc.participantslist.CallParticipantsListDialog; import org.thoughtcrime.securesms.components.webrtc.participantslist.CallParticipantsListDialog;
import org.thoughtcrime.securesms.components.webrtc.requests.CallLinkIncomingRequestSheet; import org.thoughtcrime.securesms.components.webrtc.requests.CallLinkIncomingRequestSheet;
import org.thoughtcrime.securesms.components.webrtc.v2.CallEvent;
import org.thoughtcrime.securesms.components.webrtc.v2.CallPermissionsDialogController;
import org.thoughtcrime.securesms.components.webrtc.v2.CallControlsChange; import org.thoughtcrime.securesms.components.webrtc.v2.CallControlsChange;
import org.thoughtcrime.securesms.components.webrtc.v2.CallEvent;
import org.thoughtcrime.securesms.components.webrtc.v2.CallIntent;
import org.thoughtcrime.securesms.components.webrtc.v2.CallPermissionsDialogController;
import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog; import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog;
import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.events.WebRtcViewModel; import org.thoughtcrime.securesms.events.WebRtcViewModel;
@ -132,23 +133,6 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
private static final int STANDARD_DELAY_FINISH = 1000; private static final int STANDARD_DELAY_FINISH = 1000;
private static final int VIBRATE_DURATION = 50; private static final int VIBRATE_DURATION = 50;
/**
* ANSWER the call via voice-only.
*/
public static final String ANSWER_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".ANSWER_ACTION";
/**
* ANSWER the call via video.
*/
public static final String ANSWER_VIDEO_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".ANSWER_VIDEO_ACTION";
public static final String DENY_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".DENY_ACTION";
public static final String END_CALL_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".END_CALL_ACTION";
public static final String EXTRA_ENABLE_VIDEO_IF_AVAILABLE = WebRtcCallActivity.class.getCanonicalName() + ".ENABLE_VIDEO_IF_AVAILABLE";
public static final String EXTRA_STARTED_FROM_FULLSCREEN = WebRtcCallActivity.class.getCanonicalName() + ".STARTED_FROM_FULLSCREEN";
public static final String EXTRA_STARTED_FROM_CALL_LINK = WebRtcCallActivity.class.getCanonicalName() + ".STARTED_FROM_CALL_LINK";
public static final String EXTRA_LAUNCH_IN_PIP = WebRtcCallActivity.class.getCanonicalName() + ".STARTED_FROM_CALL_LINK";
private CallParticipantsListUpdatePopupWindow participantUpdateWindow; private CallParticipantsListUpdatePopupWindow participantUpdateWindow;
private CallStateUpdatePopupWindow callStateUpdatePopupWindow; private CallStateUpdatePopupWindow callStateUpdatePopupWindow;
private CallOverflowPopupWindow callOverflowPopupWindow; private CallOverflowPopupWindow callOverflowPopupWindow;
@ -184,7 +168,8 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
@SuppressLint({ "MissingInflatedId" }) @SuppressLint({ "MissingInflatedId" })
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate(" + getIntent().getBooleanExtra(EXTRA_STARTED_FROM_FULLSCREEN, false) + ")"); CallIntent callIntent = getCallIntent();
Log.i(TAG, "onCreate(" + callIntent.isStartedFromFullScreen() + ")");
lifecycleDisposable = new LifecycleDisposable(); lifecycleDisposable = new LifecycleDisposable();
lifecycleDisposable.bindTo(this); lifecycleDisposable.bindTo(this);
@ -214,18 +199,18 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
lifecycleDisposable.add(controlsAndInfo); lifecycleDisposable.add(controlsAndInfo);
logIntent(getIntent()); logIntent(callIntent);
if (ANSWER_VIDEO_ACTION.equals(getIntent().getAction())) { if (callIntent.getAction() == CallIntent.Action.ANSWER_VIDEO) {
enableVideoIfAvailable = true; enableVideoIfAvailable = true;
} else if (ANSWER_ACTION.equals(getIntent().getAction()) || getIntent().getBooleanExtra(EXTRA_STARTED_FROM_FULLSCREEN, false)) { } else if (callIntent.getAction() == CallIntent.Action.ANSWER_AUDIO || callIntent.isStartedFromFullScreen()) {
enableVideoIfAvailable = false; enableVideoIfAvailable = false;
} else { } else {
enableVideoIfAvailable = getIntent().getBooleanExtra(EXTRA_ENABLE_VIDEO_IF_AVAILABLE, false); enableVideoIfAvailable = callIntent.shouldEnableVideoIfAvailable();
getIntent().removeExtra(EXTRA_ENABLE_VIDEO_IF_AVAILABLE); callIntent.setShouldEnableVideoIfAvailable(false);
} }
processIntent(getIntent()); processIntent(callIntent);
registerSystemPipChangeListeners(); registerSystemPipChangeListeners();
@ -302,10 +287,11 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
@Override @Override
public void onNewIntent(Intent intent) { public void onNewIntent(Intent intent) {
Log.i(TAG, "onNewIntent(" + intent.getBooleanExtra(EXTRA_STARTED_FROM_FULLSCREEN, false) + ")"); CallIntent callIntent = getCallIntent();
Log.i(TAG, "onNewIntent(" + callIntent.isStartedFromFullScreen() + ")");
super.onNewIntent(intent); super.onNewIntent(intent);
logIntent(intent); logIntent(callIntent);
processIntent(intent); processIntent(callIntent);
} }
@Override @Override
@ -373,6 +359,10 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
} }
} }
private @NonNull CallIntent getCallIntent() {
return new CallIntent(getIntent());
}
private boolean enterPipModeIfPossible() { private boolean enterPipModeIfPossible() {
if (isSystemPipEnabledAndAvailable()) { if (isSystemPipEnabledAndAvailable()) {
if (viewModel.canEnterPipMode()) { if (viewModel.canEnterPipMode()) {
@ -396,26 +386,20 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
return isSystemPipEnabledAndAvailable() && isInPictureInPictureMode(); return isSystemPipEnabledAndAvailable() && isInPictureInPictureMode();
} }
private void logIntent(@NonNull Intent intent) { private void logIntent(@NonNull CallIntent intent) {
Log.d(TAG, "Intent: Action: " + intent.getAction()); Log.d(TAG, intent.toString());
Log.d(TAG, "Intent: EXTRA_STARTED_FROM_FULLSCREEN: " + intent.getBooleanExtra(EXTRA_STARTED_FROM_FULLSCREEN, false));
Log.d(TAG, "Intent: EXTRA_ENABLE_VIDEO_IF_AVAILABLE: " + intent.getBooleanExtra(EXTRA_ENABLE_VIDEO_IF_AVAILABLE, false));
Log.d(TAG, "Intent: EXTRA_LAUNCH_IN_PIP: " + intent.getBooleanExtra(EXTRA_LAUNCH_IN_PIP, false));
} }
private void processIntent(@NonNull Intent intent) { private void processIntent(@NonNull CallIntent intent) {
if (ANSWER_ACTION.equals(intent.getAction())) { switch (intent.getAction()) {
handleAnswerWithAudio(); case ANSWER_AUDIO -> handleAnswerWithAudio();
} else if (ANSWER_VIDEO_ACTION.equals(intent.getAction())) { case ANSWER_VIDEO -> handleAnswerWithVideo();
handleAnswerWithVideo(); case DENY -> handleDenyCall();
} else if (DENY_ACTION.equals(intent.getAction())) { case END_CALL -> handleEndCall();
handleDenyCall();
} else if (END_CALL_ACTION.equals(intent.getAction())) {
handleEndCall();
} }
if (System.currentTimeMillis() - lastProcessedIntentTimestamp > TimeUnit.SECONDS.toMillis(1)) { if (System.currentTimeMillis() - lastProcessedIntentTimestamp > TimeUnit.SECONDS.toMillis(1)) {
enterPipOnResume = intent.getBooleanExtra(EXTRA_LAUNCH_IN_PIP, false); enterPipOnResume = intent.shouldLaunchInPip();
} }
lastProcessedIntentTimestamp = System.currentTimeMillis(); lastProcessedIntentTimestamp = System.currentTimeMillis();
@ -529,7 +513,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
lifecycleDisposable.add(viewModel.getInCallstatus().subscribe(this::handleInCallStatus)); lifecycleDisposable.add(viewModel.getInCallstatus().subscribe(this::handleInCallStatus));
boolean isStartedFromCallLink = getIntent().getBooleanExtra(WebRtcCallActivity.EXTRA_STARTED_FROM_CALL_LINK, false); boolean isStartedFromCallLink = getCallIntent().isStartedFromCallLink();
LiveDataUtil.combineLatest(LiveDataReactiveStreams.fromPublisher(viewModel.getCallParticipantsState().toFlowable(BackpressureStrategy.LATEST)), LiveDataUtil.combineLatest(LiveDataReactiveStreams.fromPublisher(viewModel.getCallParticipantsState().toFlowable(BackpressureStrategy.LATEST)),
orientationAndLandscapeEnabled, orientationAndLandscapeEnabled,
viewModel.getEphemeralState(), viewModel.getEphemeralState(),

View file

@ -506,6 +506,14 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
sectionHeaderPref(DSLSettingsText.from("Calling options")) sectionHeaderPref(DSLSettingsText.from("Calling options"))
switchPref(
title = DSLSettingsText.from("Use new calling UI"),
isChecked = state.newCallingUi,
onClick = {
viewModel.setUseNewCallingUi(!state.newCallingUi)
}
)
radioListPref( radioListPref(
title = DSLSettingsText.from("Audio processing method"), title = DSLSettingsText.from("Audio processing method"),
listItems = CallManager.AudioProcessingMethod.values().map { it.name }.toTypedArray(), listItems = CallManager.AudioProcessingMethod.values().map { it.name }.toTypedArray(),

View file

@ -24,5 +24,6 @@ data class InternalSettingsState(
val pnpInitialized: Boolean, val pnpInitialized: Boolean,
val useConversationItemV2ForMedia: Boolean, val useConversationItemV2ForMedia: Boolean,
val hasPendingOneTimeDonation: Boolean, val hasPendingOneTimeDonation: Boolean,
val hevcEncoding: Boolean val hevcEncoding: Boolean,
val newCallingUi: Boolean
) )

View file

@ -165,7 +165,8 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
pnpInitialized = SignalStore.misc.hasPniInitializedDevices, pnpInitialized = SignalStore.misc.hasPniInitializedDevices,
useConversationItemV2ForMedia = SignalStore.internal.useConversationItemV2Media(), useConversationItemV2ForMedia = SignalStore.internal.useConversationItemV2Media(),
hasPendingOneTimeDonation = SignalStore.inAppPayments.getPendingOneTimeDonation() != null, hasPendingOneTimeDonation = SignalStore.inAppPayments.getPendingOneTimeDonation() != null,
hevcEncoding = SignalStore.internal.hevcEncoding hevcEncoding = SignalStore.internal.hevcEncoding,
newCallingUi = SignalStore.internal.newCallingUi
) )
fun onClearOnboardingState() { fun onClearOnboardingState() {
@ -176,6 +177,11 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
StoryOnboardingDownloadJob.enqueueIfNeeded() StoryOnboardingDownloadJob.enqueueIfNeeded()
} }
fun setUseNewCallingUi(newCallingUi: Boolean) {
SignalStore.internal.newCallingUi = newCallingUi
refresh()
}
class Factory(private val repository: InternalSettingsRepository) : ViewModelProvider.Factory { class Factory(private val repository: InternalSettingsRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T { override fun <T : ViewModel> create(modelClass: Class<T>): T {
return requireNotNull(modelClass.cast(InternalSettingsViewModel(repository))) return requireNotNull(modelClass.cast(InternalSettingsViewModel(repository)))

View file

@ -7,6 +7,7 @@ package org.thoughtcrime.securesms.components.webrtc.v2
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@ -91,6 +92,7 @@ class CallActivity : BaseActivity(), CallControlsCallback {
val callInfoCallbacks = CallInfoCallbacks(this, controlsAndInfoViewModel, compositeDisposable) val callInfoCallbacks = CallInfoCallbacks(this, controlsAndInfoViewModel, compositeDisposable)
observeCallEvents() observeCallEvents()
viewModel.processCallIntent(CallIntent(intent))
setContent { setContent {
val lifecycleOwner = LocalLifecycleOwner.current val lifecycleOwner = LocalLifecycleOwner.current
@ -179,6 +181,13 @@ class CallActivity : BaseActivity(), CallControlsCallback {
} }
} }
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent != null) {
viewModel.processCallIntent(CallIntent(intent))
}
}
override fun onResume() { override fun onResume() {
Log.i(TAG, "onResume") Log.i(TAG, "onResume")
super.onResume() super.onResume()
@ -203,13 +212,9 @@ class CallActivity : BaseActivity(), CallControlsCallback {
} }
} }
/* if (viewModel.consumeEnterPipOnResume()) {
TODO // TODO enterPipModeIfPossible()
if (enterPipOnResume) {
enterPipOnResume = false;
enterPipModeIfPossible();
} }
*/
} }
override fun onPause() { override fun onPause() {

View file

@ -0,0 +1,138 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.components.webrtc.v2
import android.app.Activity
import android.content.Context
import android.content.Intent
import org.thoughtcrime.securesms.WebRtcCallActivity
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.RemoteConfig
/**
* CallIntent wraps an intent inside one of the call activities to allow for easy typed access to the necessary data within it.
*/
class CallIntent(
private val intent: Intent
) {
companion object {
private const val CALL_INTENT_PREFIX = "CallIntent"
private fun getActivityClass(): Class<out Activity> = if (RemoteConfig.newCallUi || SignalStore.internal.newCallingUi) {
CallActivity::class.java
} else {
WebRtcCallActivity::class.java
}
private fun getActionString(action: Action): String {
return "$CALL_INTENT_PREFIX.${action.code}"
}
private fun getExtraString(extra: Extra): String {
return "$CALL_INTENT_PREFIX.${extra.code}"
}
}
val action: Action by lazy { Action.fromIntent(intent) }
@get:JvmName("shouldEnableVideoIfAvailable")
var shouldEnableVideoIfAvailable: Boolean
get() = intent.getBooleanExtra(getExtraString(Extra.ENABLE_VIDEO_IF_AVAILABLE), false)
set(value) {
intent.putExtra(getExtraString(Extra.ENABLE_VIDEO_IF_AVAILABLE), value)
}
val isStartedFromFullScreen: Boolean by lazy { intent.getBooleanExtra(getExtraString(Extra.STARTED_FROM_FULLSCREEN), false) }
val isStartedFromCallLink: Boolean by lazy { intent.getBooleanExtra(getExtraString(Extra.STARTED_FROM_CALL_LINK), false) }
@get:JvmName("shouldLaunchInPip")
val shouldLaunchInPip: Boolean by lazy { intent.getBooleanExtra(getExtraString(Extra.LAUNCH_IN_PIP), false) }
override fun toString(): String {
return """
CallIntent
Action - $action
Enable video if available? $shouldEnableVideoIfAvailable
Started from full screen? $isStartedFromFullScreen
Started from call link? $isStartedFromCallLink
Launch in pip? $shouldLaunchInPip
""".trimIndent()
}
enum class Action(val code: String) {
VIEW(Intent.ACTION_VIEW),
ANSWER_AUDIO("ANSWER_ACTION"),
ANSWER_VIDEO("ANSWER_VIDEO_ACTION"),
DENY("DENY_ACTION"),
END_CALL("END_CALL_ACTION");
companion object {
fun fromIntent(intent: Intent): Action {
return intent.action?.let { a -> entries.firstOrNull { a == it.code || a == getActionString(it) } } ?: VIEW
}
}
}
private enum class Extra(val code: String) {
ENABLE_VIDEO_IF_AVAILABLE("ENABLE_VIDEO_IF_AVAILABLE"),
STARTED_FROM_FULLSCREEN("STARTED_FROM_FULLSCREEN"),
STARTED_FROM_CALL_LINK("STARTED_FROM_CALL_LINK"),
LAUNCH_IN_PIP("LAUNCH_IN_PIP")
}
/**
* Builds an intent to launch the call screen.
*/
class Builder(val context: Context) {
private val intent = Intent(context, getActivityClass())
init {
withAction(Action.VIEW)
}
fun withAddedIntentFlags(flags: Int): Builder {
intent.addFlags(flags)
return this
}
fun withIntentFlags(flags: Int): Builder {
intent.flags = flags
return this
}
fun withAction(action: Action?): Builder {
intent.action = action?.let { getActionString(action) }
return this
}
fun withEnableVideoIfAvailable(enableVideoIfAvailable: Boolean): Builder {
intent.putExtra(getExtraString(Extra.ENABLE_VIDEO_IF_AVAILABLE), enableVideoIfAvailable)
return this
}
fun withStartedFromFullScreen(startedFromFullScreen: Boolean): Builder {
intent.putExtra(getExtraString(Extra.STARTED_FROM_FULLSCREEN), startedFromFullScreen)
return this
}
fun withStartedFromCallLink(startedFromCallLink: Boolean): Builder {
intent.putExtra(getExtraString(Extra.STARTED_FROM_CALL_LINK), startedFromCallLink)
return this
}
fun withLaunchInPip(launchInPip: Boolean): Builder {
intent.putExtra(getExtraString(Extra.LAUNCH_IN_PIP), launchInPip)
return this
}
fun build(): Intent {
return intent
}
}
}

View file

@ -32,6 +32,7 @@ import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager
import org.whispersystems.signalservice.api.messages.calls.HangupMessage import org.whispersystems.signalservice.api.messages.calls.HangupMessage
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
/** /**
* Presentation logic and state holder for information that was generally done * Presentation logic and state holder for information that was generally done
@ -48,10 +49,18 @@ class CallViewModel(
private var previousEvent: WebRtcViewModel? = null private var previousEvent: WebRtcViewModel? = null
private var enableVideoIfAvailable = false private var enableVideoIfAvailable = false
private var lastProcessedIntentTimestamp = 0L
private var enterPipOnResume = false
private val internalCallScreenState = MutableStateFlow(CallScreenState()) private val internalCallScreenState = MutableStateFlow(CallScreenState())
val callScreenState: StateFlow<CallScreenState> = internalCallScreenState val callScreenState: StateFlow<CallScreenState> = internalCallScreenState
fun consumeEnterPipOnResume(): Boolean {
val enter = enterPipOnResume
enterPipOnResume = false
return enter
}
fun unregisterEventBus() { fun unregisterEventBus() {
EventBus.getDefault().unregister(this) EventBus.getDefault().unregister(this)
} }
@ -337,4 +346,31 @@ class CallViewModel(
AppDependencies.signalCallManager.selectAudioDevice(SignalAudioManager.ChosenAudioDeviceIdentifier(managerDevice)) AppDependencies.signalCallManager.selectAudioDevice(SignalAudioManager.ChosenAudioDeviceIdentifier(managerDevice))
} }
} }
fun processCallIntent(callIntent: CallIntent) {
if (callIntent.action == CallIntent.Action.ANSWER_VIDEO) {
enableVideoIfAvailable = true
} else if (callIntent.action == CallIntent.Action.ANSWER_AUDIO || callIntent.isStartedFromFullScreen) {
enableVideoIfAvailable = false
} else {
enableVideoIfAvailable = callIntent.shouldEnableVideoIfAvailable
callIntent.shouldEnableVideoIfAvailable = false
}
when (callIntent.action) {
CallIntent.Action.ANSWER_AUDIO -> startCall(false)
CallIntent.Action.ANSWER_VIDEO -> startCall(true)
CallIntent.Action.DENY -> deny()
CallIntent.Action.END_CALL -> hangup()
CallIntent.Action.VIEW -> Unit
}
// Prevents some issues around intent re-use when dealing with picture-in-picture.
val now = System.currentTimeMillis()
if (now - lastProcessedIntentTimestamp > 1.seconds.inWholeMilliseconds) {
enterPipOnResume = callIntent.shouldLaunchInPip
}
lastProcessedIntentTimestamp = now
}
} }

View file

@ -33,6 +33,7 @@ public final class InternalValues extends SignalStoreValues {
public static final String FORCE_ENTER_RESTORE_V2_FLOW = "internal.force_enter_restore_v2_flow"; public static final String FORCE_ENTER_RESTORE_V2_FLOW = "internal.force_enter_restore_v2_flow";
public static final String WEB_SOCKET_SHADOWING_STATS = "internal.web_socket_shadowing_stats"; public static final String WEB_SOCKET_SHADOWING_STATS = "internal.web_socket_shadowing_stats";
public static final String ENCODE_HEVC = "internal.hevc_encoding"; public static final String ENCODE_HEVC = "internal.hevc_encoding";
public static final String NEW_CALL_UI = "internal.new.call.ui";
InternalValues(KeyValueStore store) { InternalValues(KeyValueStore store) {
super(store); super(store);
@ -192,6 +193,14 @@ public final class InternalValues extends SignalStoreValues {
return getBoolean(ENCODE_HEVC, false); return getBoolean(ENCODE_HEVC, false);
} }
public void setNewCallingUi(boolean enabled) {
putBoolean(NEW_CALL_UI, enabled);
}
public boolean getNewCallingUi() {
return getBoolean(NEW_CALL_UI, false);
}
public void setLastScrollPosition(int position) { public void setLastScrollPosition(int position) {
putInteger(LAST_SCROLL_POSITION, position); putInteger(LAST_SCROLL_POSITION, position);
} }

View file

@ -6,7 +6,7 @@ import android.telecom.CallAudioState
import android.telecom.Connection import android.telecom.Connection
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import org.signal.core.util.logging.Log import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.WebRtcCallActivity import org.thoughtcrime.securesms.components.webrtc.v2.CallIntent
import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.permissions.Permissions
import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.recipients.RecipientId
@ -66,9 +66,10 @@ class AndroidCallConnection(
if (Permissions.hasAll(context, android.Manifest.permission.RECORD_AUDIO)) { if (Permissions.hasAll(context, android.Manifest.permission.RECORD_AUDIO)) {
AppDependencies.signalCallManager.acceptCall(false) AppDependencies.signalCallManager.acceptCall(false)
} else { } else {
val intent = Intent(context, WebRtcCallActivity::class.java) val intent = CallIntent.Builder(context)
intent.action = if (isVideoCall) WebRtcCallActivity.ANSWER_VIDEO_ACTION else WebRtcCallActivity.ANSWER_ACTION .withAddedIntentFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.flags = intent.flags or Intent.FLAG_ACTIVITY_NEW_TASK .withAction(if (isVideoCall) CallIntent.Action.ANSWER_VIDEO else CallIntent.Action.ANSWER_AUDIO)
.build()
context.startActivity(intent) context.startActivity(intent)
} }
} }

View file

@ -36,6 +36,7 @@ import org.signal.ringrtc.PeekInfo;
import org.signal.ringrtc.Remote; import org.signal.ringrtc.Remote;
import org.signal.storageservice.protos.groups.GroupExternalCredential; import org.signal.storageservice.protos.groups.GroupExternalCredential;
import org.thoughtcrime.securesms.WebRtcCallActivity; import org.thoughtcrime.securesms.WebRtcCallActivity;
import org.thoughtcrime.securesms.components.webrtc.v2.CallIntent;
import org.thoughtcrime.securesms.crypto.SealedSenderAccessUtil; import org.thoughtcrime.securesms.crypto.SealedSenderAccessUtil;
import org.thoughtcrime.securesms.database.CallLinkTable; import org.thoughtcrime.securesms.database.CallLinkTable;
import org.thoughtcrime.securesms.database.CallTable; import org.thoughtcrime.securesms.database.CallTable;
@ -1253,10 +1254,12 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
WebRtcViewModel.State callState = s.getCallInfoState().getCallState(); WebRtcViewModel.State callState = s.getCallInfoState().getCallState();
if (callState.getInOngoingCall()) { if (callState.getInOngoingCall()) {
Intent intent = new Intent(context, WebRtcCallActivity.class); context.startActivity(
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); new CallIntent.Builder(context)
intent.putExtra(WebRtcCallActivity.EXTRA_LAUNCH_IN_PIP, true); .withIntentFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent); .withLaunchInPip(true)
.build()
);
} }
return s; return s;

View file

@ -32,6 +32,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.WebRtcCallActivity; import org.thoughtcrime.securesms.WebRtcCallActivity;
import org.thoughtcrime.securesms.calls.links.CallLinks; import org.thoughtcrime.securesms.calls.links.CallLinks;
import org.thoughtcrime.securesms.components.webrtc.v2.CallActivity; import org.thoughtcrime.securesms.components.webrtc.v2.CallActivity;
import org.thoughtcrime.securesms.components.webrtc.v2.CallIntent;
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery; import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery;
import org.thoughtcrime.securesms.conversation.ConversationIntents; import org.thoughtcrime.securesms.conversation.ConversationIntents;
import org.thoughtcrime.securesms.database.CallLinkTable; import org.thoughtcrime.securesms.database.CallLinkTable;
@ -397,11 +398,11 @@ public class CommunicationActions {
MessageSender.onMessageSent(); MessageSender.onMessageSent();
Intent activityIntent = new Intent(callContext.getContext(), getCallActivityClass()); callContext.startActivity(
new CallIntent.Builder(callContext.getContext())
activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); .withIntentFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.build()
callContext.startActivity(activityIntent); );
}) })
.execute(); .execute();
} }
@ -409,13 +410,13 @@ public class CommunicationActions {
private static void startVideoCallInternal(@NonNull CallContext callContext, @NonNull Recipient recipient, boolean fromCallLink) { private static void startVideoCallInternal(@NonNull CallContext callContext, @NonNull Recipient recipient, boolean fromCallLink) {
AppDependencies.getSignalCallManager().startPreJoinCall(recipient); AppDependencies.getSignalCallManager().startPreJoinCall(recipient);
Intent activityIntent = new Intent(callContext.getContext(), getCallActivityClass()); callContext.startActivity(
new CallIntent.Builder(callContext.getContext())
activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .withIntentFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(WebRtcCallActivity.EXTRA_ENABLE_VIDEO_IF_AVAILABLE, true) .withEnableVideoIfAvailable(true)
.putExtra(WebRtcCallActivity.EXTRA_STARTED_FROM_CALL_LINK, fromCallLink); .withStartedFromCallLink(fromCallLink)
.build()
callContext.startActivity(activityIntent); );
} }
private static void handleE164Link(Activity activity, String e164) { private static void handleE164Link(Activity activity, String e164) {
@ -479,10 +480,6 @@ public class CommunicationActions {
}); });
} }
private static Class<? extends Activity> getCallActivityClass() {
return RemoteConfig.newCallUi() ? CallActivity.class : WebRtcCallActivity.class;
}
private interface CallContext { private interface CallContext {
@NonNull Permissions.PermissionsBuilder getPermissionsBuilder(); @NonNull Permissions.PermissionsBuilder getPermissionsBuilder();
void startActivity(@NonNull Intent intent); void startActivity(@NonNull Intent intent);

View file

@ -15,10 +15,9 @@ import androidx.core.app.Person;
import org.signal.core.util.PendingIntentFlags; import org.signal.core.util.PendingIntentFlags;
import org.thoughtcrime.securesms.MainActivity; import org.thoughtcrime.securesms.MainActivity;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.WebRtcCallActivity; import org.thoughtcrime.securesms.components.webrtc.v2.CallIntent;
import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.webrtc.ActiveCallManager;
import org.thoughtcrime.securesms.service.webrtc.WebRtcCallService; import org.thoughtcrime.securesms.service.webrtc.WebRtcCallService;
import org.thoughtcrime.securesms.util.ConversationUtil; import org.thoughtcrime.securesms.util.ConversationUtil;
@ -49,13 +48,13 @@ public class CallNotificationBuilder {
private enum LaunchCallScreenIntentState { private enum LaunchCallScreenIntentState {
CONTENT(null, 0), CONTENT(null, 0),
AUDIO(WebRtcCallActivity.ANSWER_ACTION, 1), AUDIO(CallIntent.Action.ANSWER_AUDIO, 1),
VIDEO(WebRtcCallActivity.ANSWER_VIDEO_ACTION, 2); VIDEO(CallIntent.Action.ANSWER_VIDEO, 2);
final @Nullable String action; final @Nullable CallIntent.Action action;
final int requestCode; final int requestCode;
LaunchCallScreenIntentState(@Nullable String action, int requestCode) { LaunchCallScreenIntentState(@Nullable CallIntent.Action action, int requestCode) {
this.action = action; this.action = action;
this.requestCode = requestCode; this.requestCode = requestCode;
} }
@ -219,17 +218,17 @@ public class CallNotificationBuilder {
} }
private static PendingIntent getActivityPendingIntent(@NonNull Context context, @NonNull LaunchCallScreenIntentState launchCallScreenIntentState) { private static PendingIntent getActivityPendingIntent(@NonNull Context context, @NonNull LaunchCallScreenIntentState launchCallScreenIntentState) {
Intent intent = new Intent(context, WebRtcCallActivity.class); CallIntent.Builder builder = new CallIntent.Builder(context);
intent.setAction(launchCallScreenIntentState.action); builder.withAction(launchCallScreenIntentState.action);
if (launchCallScreenIntentState == LaunchCallScreenIntentState.CONTENT) { if (launchCallScreenIntentState == LaunchCallScreenIntentState.CONTENT) {
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); builder.withIntentFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
} }
intent.putExtra(WebRtcCallActivity.EXTRA_STARTED_FROM_FULLSCREEN, launchCallScreenIntentState == LaunchCallScreenIntentState.CONTENT); builder.withStartedFromFullScreen(launchCallScreenIntentState == LaunchCallScreenIntentState.CONTENT);
intent.putExtra(WebRtcCallActivity.EXTRA_ENABLE_VIDEO_IF_AVAILABLE, false); builder.withEnableVideoIfAvailable(false);
return PendingIntent.getActivity(context, launchCallScreenIntentState.requestCode, intent, PendingIntentFlags.updateCurrent()); return PendingIntent.getActivity(context, launchCallScreenIntentState.requestCode, builder.build(), PendingIntentFlags.updateCurrent());
} }
private static boolean deviceVersionSupportsIncomingCallStyle() { private static boolean deviceVersionSupportsIncomingCallStyle() {