mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-11 19:07:40 +00:00
Add one on one calls over clearnet (#864)
* feat: adding basic webrtc deps and test activity * more testing code * feat: add protos and bump version * feat: added basic call functionality * feat: adding UI and flipping cameras * feat: add stats and starting call bottom sheet * feat: hanging up and bottom sheet behaviors should work now * feat: add call stats report on frontend * feat: add relay toggle for answer and offer * fix: add keep screen on and more end call message on back pressed / on finish * refactor: removing and replacing dagger 1 dep with android hilt * feat: include latest proto * feat: update to utilise call ID * feat: add stun and turn * refactor: playing around with deps and transport types * feat: adding call service functionality and permissions for calls * feat: add call manager and more static intent building functions for WebRtcCallService.kt * feat: adding ringers and more audio boilerplate * feat: audio manager call service boilerplate * feat: update kotlin and add in call view model and more management functions * refactor: moving call code around to service and viewmodel interactions * feat: plugging CallManager.kt into view model and service, fixing up dependencies * feat: implementing more WebRtcCallService.kt functions and handlers for actions as well as lifecycle * feat: adding more lifecycle vm and callmanager / call service functionality * feat: adding more command handlers in WebRtcCallService.kt * feat: more commands handled, adding lock manager and bluetooth permissions * feat: adding remainder of basic functionality to services and CallManager.kt * feat: hooking up calls and fixing broken dependencies and compile errors * fix: add timestamp to incoming call * feat: some connection and service launching / ring lifecycle * feat: call establishing and displaying * fix: fixing call connect flows * feat: ringers and better state handling * feat: updating call layout * feat: add fixes to bluetooth and begin the network renegotiation * feat: add call related permissions and more network handover tests * fix: don't display call option in conversation and don't show notification if option not enabled * fix: incoming ringer fix on receiving call, call notification priorities and notification channel update * build: update build number for testing * fix: bluetooth auto-connection and re-connection fixes, removing finished todos, allowing self-send call messages for deduping answers * feat: add pre-offer information and action handling in web rtc call service * refactor: discard offer messages from non-matching pre-offers we are already expecting * build: build numbers and version name update * feat: handle discarding pending calls from linked devices * feat: add signing props to release config build * docs: fix comment on time being 300s (5m) instead of 30s * feat: adding call messages for incoming/outgoing/missed * refactor: handle in-thread call notifications better and replace deny button intent with denyCallIntent instead of hangup * feat: add a hangup via data channel message * feat: process microphone enabled events and remove debuggable from build.gradle * feat: add first call notification * refactor: set the buttons to match iOS in terms of enable disable and colours * refactor: change the call logos in control messages * refactor: more bluetooth improvements * refactor: move start ringer and init of audio manager to CallManager.kt and string fix up * build: remove debuggable for release build * refactor: replace call icons * feat: adding a call time display * refactor: change the call time to update every second * refactor: testing out the full screen intents * refactor: wrapper use corrected session description, set title to recipient displayName, indicate session calls * fix: crash on view with a parent already attached * refactor: aspect ratio fit preserved * refactor: add wantsToAnswer ability in pre-init for fullscreenintent * refactor: prevent calls from non hasSent participants * build: update gradle code * refactor: replace timeout schedule with a seconds count * fix: various bug fixes for calls * fix: remove end call from busy * refactor: use answerCall instead of manual intent building again * build: new version * feat: add silenced notifications for call notification builder. check pre-offer and connecting state for pending connection * build: update build number * fix: text color uses overridden style value * fix: remove wrap content for renderers and look more at recovering from network switches * build: update build number * refactor: remove whitespace * build: update build number * refactor: used shared number for BatchMessageReceiveJob.kt parameter across pollers * fix: glide in update crash * fix: bug fixes for self-send answer / hangup messages * build: update build number * build: update build.gradle number * refactor: compile errors and refactoring to view binding * fix: set the content to binding.root view * build: increase build number * build: update build numbers * feat: adding base for rotation and picking random subset of turn servers * feat: starting the screen rotation processing * feat: setting up rotation for the remote render view * refactor: applying rotation and mirroring based on front / rear cameras that wraps nicely, only scale reworking needed * refactor: calls video stretching but consistent * refactor: state machine and tests for the transition events * feat: new call state processing * refactor: adding reconnecting logic and visuals * feat: state machine reconnect logic wip * feat: add reconnecting and merge fixes * feat: check new session based off current state * feat: reconnection logic works correctly now * refactor: reduce TIMEOUT_SECONDS to 30 from 90 * feat: reset peer connection on DC to prevent ICE messages from old connection or stale state in reconnecting * refactor: add null case * fix: set approved on new outgoing threads, use approved more deeply and invalidate the options menu on recipient modified. Add approvedMe flag toggles for visible message receive * fix: add name update in action bar on modified, change where approvedMe is set * build: increment build number * build: update build number * fix: merge compile errors and increment build number * refactor: remove negotiation based on which party dropped connection * refactor: call reconnection improvement tested cross platform to re-establish * refactor: failed and disconnect events only handled if either the reconnect or the timeout runnables are not set * build: update version number * fix: reduce timeout * fix: fixes the incoming hangup logic for linked devices * refactor: match iOS styling for call activity closer * chore: upgrade build numbers * feat: add in call settings dialog for if calls is disabled in conversation * feat: add a first call missed control message and info popup with link to privacy settings * fix: looking at crash for specific large transaction in NotificationManager * refactor: removing the people in case transaction size reduces to fix notif crash * fix: comment out the entire send multiple to see if it fixes the issue * refactor: revert to including the full notification process in a try/catch to handle weird responses from NotificationManager * fix: add in notification settings prompt for calls and try to fall back to dirty full screen intent / start activity if we're allowed * build: upgrade build number
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
package org.session.libsession.database
|
||||
|
||||
interface CallDataProvider {
|
||||
// answer/offer for call by UUID
|
||||
// recipient info for call by UUID
|
||||
|
||||
}
|
@@ -2,6 +2,7 @@ package org.session.libsession.database
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import org.session.libsession.messaging.calls.CallMessageType
|
||||
import org.session.libsession.messaging.contacts.Contact
|
||||
import org.session.libsession.messaging.jobs.AttachmentUploadJob
|
||||
import org.session.libsession.messaging.jobs.Job
|
||||
@@ -159,4 +160,6 @@ interface StorageProtocol {
|
||||
fun insertMessageRequestResponse(response: MessageRequestResponse)
|
||||
fun setRecipientApproved(recipient: Recipient, approved: Boolean)
|
||||
fun setRecipientApprovedMe(recipient: Recipient, approvedMe: Boolean)
|
||||
fun insertCallMessage(senderPublicKey: String, callMessageType: CallMessageType, sentTimestamp: Long)
|
||||
fun conversationHasOutgoing(userPublicKey: String): Boolean
|
||||
}
|
||||
|
@@ -0,0 +1,8 @@
|
||||
package org.session.libsession.messaging.calls
|
||||
|
||||
enum class CallMessageType {
|
||||
CALL_MISSED,
|
||||
CALL_INCOMING,
|
||||
CALL_OUTGOING,
|
||||
CALL_FIRST_MISSED,
|
||||
}
|
@@ -31,6 +31,8 @@ class BatchMessageReceiveJob(
|
||||
const val TAG = "BatchMessageReceiveJob"
|
||||
const val KEY = "BatchMessageReceiveJob"
|
||||
|
||||
const val BATCH_DEFAULT_NUMBER = 50
|
||||
|
||||
// Keys used for database storage
|
||||
private val NUM_MESSAGES_KEY = "numMessages"
|
||||
private val DATA_KEY = "data"
|
||||
|
@@ -0,0 +1,119 @@
|
||||
package org.session.libsession.messaging.messages.control
|
||||
|
||||
import org.session.libsignal.protos.SignalServiceProtos
|
||||
import org.session.libsignal.protos.SignalServiceProtos.CallMessage.Type.*
|
||||
import org.session.libsignal.utilities.Log
|
||||
import java.util.*
|
||||
|
||||
class CallMessage(): ControlMessage() {
|
||||
var type: SignalServiceProtos.CallMessage.Type? = null
|
||||
var sdps: List<String> = listOf()
|
||||
var sdpMLineIndexes: List<Int> = listOf()
|
||||
var sdpMids: List<String> = listOf()
|
||||
var callId: UUID? = null
|
||||
|
||||
override val isSelfSendValid: Boolean get() = type in arrayOf(ANSWER, END_CALL)
|
||||
|
||||
override val ttl: Long = 300000L // 5m
|
||||
|
||||
override fun isValid(): Boolean = super.isValid() && type != null && callId != null
|
||||
&& (!sdps.isNullOrEmpty() || type in listOf(END_CALL, PRE_OFFER))
|
||||
|
||||
constructor(type: SignalServiceProtos.CallMessage.Type,
|
||||
sdps: List<String>,
|
||||
sdpMLineIndexes: List<Int>,
|
||||
sdpMids: List<String>,
|
||||
callId: UUID) : this() {
|
||||
this.type = type
|
||||
this.sdps = sdps
|
||||
this.sdpMLineIndexes = sdpMLineIndexes
|
||||
this.sdpMids = sdpMids
|
||||
this.callId = callId
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "CallMessage"
|
||||
|
||||
fun answer(sdp: String, callId: UUID) = CallMessage(ANSWER,
|
||||
listOf(sdp),
|
||||
listOf(),
|
||||
listOf(),
|
||||
callId
|
||||
)
|
||||
|
||||
fun preOffer(callId: UUID) = CallMessage(PRE_OFFER,
|
||||
listOf(),
|
||||
listOf(),
|
||||
listOf(),
|
||||
callId
|
||||
)
|
||||
|
||||
fun offer(sdp: String, callId: UUID) = CallMessage(OFFER,
|
||||
listOf(sdp),
|
||||
listOf(),
|
||||
listOf(),
|
||||
callId
|
||||
)
|
||||
|
||||
fun endCall(callId: UUID) = CallMessage(END_CALL, emptyList(), emptyList(), emptyList(), callId)
|
||||
|
||||
fun fromProto(proto: SignalServiceProtos.Content): CallMessage? {
|
||||
val callMessageProto = if (proto.hasCallMessage()) proto.callMessage else return null
|
||||
val type = callMessageProto.type
|
||||
val sdps = callMessageProto.sdpsList
|
||||
val sdpMLineIndexes = callMessageProto.sdpMLineIndexesList
|
||||
val sdpMids = callMessageProto.sdpMidsList
|
||||
val callId = UUID.fromString(callMessageProto.uuid)
|
||||
return CallMessage(type,sdps, sdpMLineIndexes, sdpMids, callId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toProto(): SignalServiceProtos.Content? {
|
||||
val nonNullType = type ?: run {
|
||||
Log.w(TAG,"Couldn't construct call message request proto from: $this")
|
||||
return null
|
||||
}
|
||||
|
||||
val callMessage = SignalServiceProtos.CallMessage.newBuilder()
|
||||
.setType(nonNullType)
|
||||
.addAllSdps(sdps)
|
||||
.addAllSdpMLineIndexes(sdpMLineIndexes)
|
||||
.addAllSdpMids(sdpMids)
|
||||
.setUuid(callId!!.toString())
|
||||
|
||||
return SignalServiceProtos.Content.newBuilder()
|
||||
.setCallMessage(
|
||||
callMessage
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as CallMessage
|
||||
|
||||
if (callId != other.callId) return false
|
||||
if (type != other.type) return false
|
||||
if (sdps != other.sdps) return false
|
||||
if (sdpMLineIndexes != other.sdpMLineIndexes) return false
|
||||
if (sdpMids != other.sdpMids) return false
|
||||
if (isSelfSendValid != other.isSelfSendValid) return false
|
||||
if (ttl != other.ttl) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = type?.hashCode() ?: 0
|
||||
result = 31 * result + sdps.hashCode()
|
||||
result = 31 * result + sdpMLineIndexes.hashCode()
|
||||
result = 31 * result + sdpMids.hashCode()
|
||||
result = 31 * result + isSelfSendValid.hashCode()
|
||||
result = 31 * result + ttl.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -5,6 +5,7 @@ import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.session.libsession.messaging.calls.CallMessageType;
|
||||
import org.session.libsession.messaging.messages.visible.OpenGroupInvitation;
|
||||
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
||||
import org.session.libsession.utilities.Address;
|
||||
@@ -41,13 +42,25 @@ public class IncomingTextMessage implements Parcelable {
|
||||
private final int subscriptionId;
|
||||
private final long expiresInMillis;
|
||||
private final boolean unidentified;
|
||||
private final int callType;
|
||||
|
||||
private boolean isOpenGroupInvitation = false;
|
||||
|
||||
public IncomingTextMessage(Address sender, int senderDeviceId, long sentTimestampMillis,
|
||||
String encodedBody, Optional<SignalServiceGroup> group,
|
||||
long expiresInMillis, boolean unidentified) {
|
||||
this(sender, senderDeviceId, sentTimestampMillis, encodedBody, group, expiresInMillis, unidentified, -1);
|
||||
}
|
||||
|
||||
public IncomingTextMessage(Address sender, int senderDeviceId, long sentTimestampMillis,
|
||||
String encodedBody, Optional<SignalServiceGroup> group,
|
||||
long expiresInMillis, boolean unidentified)
|
||||
{
|
||||
long expiresInMillis, boolean unidentified, int callType) {
|
||||
this(sender, senderDeviceId, sentTimestampMillis, encodedBody, group, expiresInMillis, unidentified, callType, true);
|
||||
}
|
||||
|
||||
public IncomingTextMessage(Address sender, int senderDeviceId, long sentTimestampMillis,
|
||||
String encodedBody, Optional<SignalServiceGroup> group,
|
||||
long expiresInMillis, boolean unidentified, int callType, boolean isPush) {
|
||||
this.message = encodedBody;
|
||||
this.sender = sender;
|
||||
this.senderDeviceId = senderDeviceId;
|
||||
@@ -56,10 +69,11 @@ public class IncomingTextMessage implements Parcelable {
|
||||
this.replyPathPresent = true;
|
||||
this.pseudoSubject = "";
|
||||
this.sentTimestampMillis = sentTimestampMillis;
|
||||
this.push = true;
|
||||
this.push = isPush;
|
||||
this.subscriptionId = -1;
|
||||
this.expiresInMillis = expiresInMillis;
|
||||
this.unidentified = unidentified;
|
||||
this.callType = callType;
|
||||
|
||||
if (group.isPresent()) {
|
||||
this.groupId = Address.fromSerialized(GroupUtil.getEncodedId(group.get()));
|
||||
@@ -69,36 +83,39 @@ public class IncomingTextMessage implements Parcelable {
|
||||
}
|
||||
|
||||
public IncomingTextMessage(Parcel in) {
|
||||
this.message = in.readString();
|
||||
this.sender = in.readParcelable(IncomingTextMessage.class.getClassLoader());
|
||||
this.senderDeviceId = in.readInt();
|
||||
this.protocol = in.readInt();
|
||||
this.serviceCenterAddress = in.readString();
|
||||
this.replyPathPresent = (in.readInt() == 1);
|
||||
this.pseudoSubject = in.readString();
|
||||
this.sentTimestampMillis = in.readLong();
|
||||
this.groupId = in.readParcelable(IncomingTextMessage.class.getClassLoader());
|
||||
this.push = (in.readInt() == 1);
|
||||
this.subscriptionId = in.readInt();
|
||||
this.expiresInMillis = in.readLong();
|
||||
this.unidentified = in.readInt() == 1;
|
||||
this.message = in.readString();
|
||||
this.sender = in.readParcelable(IncomingTextMessage.class.getClassLoader());
|
||||
this.senderDeviceId = in.readInt();
|
||||
this.protocol = in.readInt();
|
||||
this.serviceCenterAddress = in.readString();
|
||||
this.replyPathPresent = (in.readInt() == 1);
|
||||
this.pseudoSubject = in.readString();
|
||||
this.sentTimestampMillis = in.readLong();
|
||||
this.groupId = in.readParcelable(IncomingTextMessage.class.getClassLoader());
|
||||
this.push = (in.readInt() == 1);
|
||||
this.subscriptionId = in.readInt();
|
||||
this.expiresInMillis = in.readLong();
|
||||
this.unidentified = in.readInt() == 1;
|
||||
this.isOpenGroupInvitation = in.readInt() == 1;
|
||||
this.callType = in.readInt();
|
||||
}
|
||||
|
||||
public IncomingTextMessage(IncomingTextMessage base, String newBody) {
|
||||
this.message = newBody;
|
||||
this.sender = base.getSender();
|
||||
this.senderDeviceId = base.getSenderDeviceId();
|
||||
this.protocol = base.getProtocol();
|
||||
this.serviceCenterAddress = base.getServiceCenterAddress();
|
||||
this.replyPathPresent = base.isReplyPathPresent();
|
||||
this.pseudoSubject = base.getPseudoSubject();
|
||||
this.sentTimestampMillis = base.getSentTimestampMillis();
|
||||
this.groupId = base.getGroupId();
|
||||
this.push = base.isPush();
|
||||
this.subscriptionId = base.getSubscriptionId();
|
||||
this.expiresInMillis = base.getExpiresIn();
|
||||
this.unidentified = base.isUnidentified();
|
||||
this.isOpenGroupInvitation= base.isOpenGroupInvitation();
|
||||
this.message = newBody;
|
||||
this.sender = base.getSender();
|
||||
this.senderDeviceId = base.getSenderDeviceId();
|
||||
this.protocol = base.getProtocol();
|
||||
this.serviceCenterAddress = base.getServiceCenterAddress();
|
||||
this.replyPathPresent = base.isReplyPathPresent();
|
||||
this.pseudoSubject = base.getPseudoSubject();
|
||||
this.sentTimestampMillis = base.getSentTimestampMillis();
|
||||
this.groupId = base.getGroupId();
|
||||
this.push = base.isPush();
|
||||
this.subscriptionId = base.getSubscriptionId();
|
||||
this.expiresInMillis = base.getExpiresIn();
|
||||
this.unidentified = base.isUnidentified();
|
||||
this.isOpenGroupInvitation = base.isOpenGroupInvitation();
|
||||
this.callType = base.callType;
|
||||
}
|
||||
|
||||
public static IncomingTextMessage from(VisibleMessage message,
|
||||
@@ -121,6 +138,13 @@ public class IncomingTextMessage implements Parcelable {
|
||||
return incomingTextMessage;
|
||||
}
|
||||
|
||||
public static IncomingTextMessage fromCallInfo(CallMessageType callMessageType,
|
||||
Address sender,
|
||||
Optional<SignalServiceGroup> group,
|
||||
long sentTimestamp) {
|
||||
return new IncomingTextMessage(sender, 1, sentTimestamp, null, group, 0, false, callMessageType.ordinal(), false);
|
||||
}
|
||||
|
||||
public int getSubscriptionId() {
|
||||
return subscriptionId;
|
||||
}
|
||||
@@ -183,6 +207,18 @@ public class IncomingTextMessage implements Parcelable {
|
||||
|
||||
public boolean isOpenGroupInvitation() { return isOpenGroupInvitation; }
|
||||
|
||||
public boolean isCallInfo() {
|
||||
int callMessageTypeLength = CallMessageType.values().length;
|
||||
return callType >= 0 && callType < callMessageTypeLength;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CallMessageType getCallType() {
|
||||
int callTypeLength = CallMessageType.values().length;
|
||||
if (callType < 0 || callType >= callTypeLength) return null;
|
||||
return CallMessageType.values()[callType];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
@@ -202,5 +238,7 @@ public class IncomingTextMessage implements Parcelable {
|
||||
out.writeInt(push ? 1 : 0);
|
||||
out.writeInt(subscriptionId);
|
||||
out.writeInt(unidentified ? 1 : 0);
|
||||
out.writeInt(isOpenGroupInvitation ? 1 : 0);
|
||||
out.writeInt(callType);
|
||||
}
|
||||
}
|
||||
|
@@ -96,6 +96,7 @@ object MessageReceiver {
|
||||
ConfigurationMessage.fromProto(proto) ?:
|
||||
UnsendRequest.fromProto(proto) ?:
|
||||
MessageRequestResponse.fromProto(proto) ?:
|
||||
CallMessage.fromProto(proto) ?:
|
||||
VisibleMessage.fromProto(proto) ?: throw Error.UnknownMessage
|
||||
// Ignore self send if needed
|
||||
if (!message.isSelfSendValid && sender == userPublicKey) throw Error.SelfSend
|
||||
|
@@ -8,10 +8,7 @@ import org.session.libsession.messaging.jobs.MessageSendJob
|
||||
import org.session.libsession.messaging.jobs.NotifyPNServerJob
|
||||
import org.session.libsession.messaging.messages.Destination
|
||||
import org.session.libsession.messaging.messages.Message
|
||||
import org.session.libsession.messaging.messages.control.ClosedGroupControlMessage
|
||||
import org.session.libsession.messaging.messages.control.ConfigurationMessage
|
||||
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate
|
||||
import org.session.libsession.messaging.messages.control.UnsendRequest
|
||||
import org.session.libsession.messaging.messages.control.*
|
||||
import org.session.libsession.messaging.messages.visible.*
|
||||
import org.session.libsession.messaging.open_groups.*
|
||||
import org.session.libsession.messaging.utilities.MessageWrapper
|
||||
@@ -165,7 +162,7 @@ object MessageSender {
|
||||
val hash = it["hash"] as? String
|
||||
message.serverHash = hash
|
||||
handleSuccessfulMessageSend(message, destination, isSyncMessage)
|
||||
var shouldNotify = ((message is VisibleMessage || message is UnsendRequest) && !isSyncMessage)
|
||||
var shouldNotify = ((message is VisibleMessage || message is UnsendRequest || message is CallMessage) && !isSyncMessage)
|
||||
/*
|
||||
if (message is ClosedGroupControlMessage && message.kind is ClosedGroupControlMessage.Kind.New) {
|
||||
shouldNotify = true
|
||||
|
@@ -6,6 +6,7 @@ import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.messaging.jobs.AttachmentDownloadJob
|
||||
import org.session.libsession.messaging.jobs.JobQueue
|
||||
import org.session.libsession.messaging.messages.Message
|
||||
import org.session.libsession.messaging.messages.control.CallMessage
|
||||
import org.session.libsession.messaging.messages.control.ClosedGroupControlMessage
|
||||
import org.session.libsession.messaging.messages.control.ConfigurationMessage
|
||||
import org.session.libsession.messaging.messages.control.DataExtractionNotification
|
||||
@@ -22,6 +23,7 @@ import org.session.libsession.messaging.sending_receiving.link_preview.LinkPrevi
|
||||
import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI
|
||||
import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2
|
||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
|
||||
import org.session.libsession.messaging.utilities.WebRtcUtils
|
||||
import org.session.libsession.snode.SnodeAPI
|
||||
import org.session.libsession.utilities.Address
|
||||
import org.session.libsession.utilities.GroupRecord
|
||||
@@ -60,6 +62,7 @@ fun MessageReceiver.handle(message: Message, proto: SignalServiceProtos.Content,
|
||||
is UnsendRequest -> handleUnsendRequest(message)
|
||||
is MessageRequestResponse -> handleMessageRequestResponse(message)
|
||||
is VisibleMessage -> handleVisibleMessage(message, proto, openGroupID)
|
||||
is CallMessage -> handleCallMessage(message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +72,11 @@ private fun MessageReceiver.handleReadReceipt(message: ReadReceipt) {
|
||||
SSKEnvironment.shared.readReceiptManager.processReadReceipts(context, message.sender!!, message.timestamps!!, message.receivedTimestamp!!)
|
||||
}
|
||||
|
||||
private fun MessageReceiver.handleCallMessage(message: CallMessage) {
|
||||
// TODO: refactor this out to persistence, just to help debug the flow and send/receive in synchronous testing
|
||||
WebRtcUtils.SIGNAL_QUEUE.trySend(message)
|
||||
}
|
||||
|
||||
private fun MessageReceiver.handleTypingIndicator(message: TypingIndicator) {
|
||||
when (message.kind!!) {
|
||||
TypingIndicator.Kind.STARTED -> showTypingIndicatorIfNeeded(message.sender!!)
|
||||
|
@@ -4,8 +4,10 @@ import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.functional.bind
|
||||
import nl.komponents.kovenant.functional.map
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.messaging.jobs.BatchMessageReceiveJob
|
||||
import org.session.libsession.messaging.jobs.JobQueue
|
||||
import org.session.libsession.messaging.jobs.MessageReceiveJob
|
||||
import org.session.libsession.messaging.jobs.MessageReceiveParameters
|
||||
import org.session.libsession.snode.SnodeAPI
|
||||
import org.session.libsession.utilities.GroupUtil
|
||||
import org.session.libsignal.crypto.getRandomElementOrNull
|
||||
@@ -100,8 +102,12 @@ class ClosedGroupPollerV2 {
|
||||
}
|
||||
promise.success { envelopes ->
|
||||
if (!isPolling(groupPublicKey)) { return@success }
|
||||
envelopes.iterator().forEach { (envelope, serverHash) ->
|
||||
val job = MessageReceiveJob(envelope.toByteArray(), serverHash)
|
||||
|
||||
val parameters = envelopes.map { (envelope, serverHash) ->
|
||||
MessageReceiveParameters(envelope.toByteArray(), serverHash = serverHash)
|
||||
}
|
||||
parameters.chunked(BatchMessageReceiveJob.BATCH_DEFAULT_NUMBER).iterator().forEach { chunk ->
|
||||
val job = BatchMessageReceiveJob(chunk)
|
||||
JobQueue.shared.add(job)
|
||||
}
|
||||
}
|
||||
|
@@ -73,7 +73,7 @@ class OpenGroupPollerV2(private val server: String, private val executorService:
|
||||
builder.build() to message.serverID
|
||||
}
|
||||
|
||||
envelopes.chunked(256).forEach { list ->
|
||||
envelopes.chunked(BatchMessageReceiveJob.BATCH_DEFAULT_NUMBER).forEach { list ->
|
||||
val parameters = list.map { (message, serverId) ->
|
||||
MessageReceiveParameters(message.toByteArray(), openGroupMessageServerID = serverId)
|
||||
}
|
||||
|
@@ -95,7 +95,7 @@ class Poller {
|
||||
val parameters = messages.map { (envelope, serverHash) ->
|
||||
MessageReceiveParameters(envelope.toByteArray(), serverHash = serverHash)
|
||||
}
|
||||
parameters.chunked(20).forEach { chunk ->
|
||||
parameters.chunked(BatchMessageReceiveJob.BATCH_DEFAULT_NUMBER).forEach { chunk ->
|
||||
val job = BatchMessageReceiveJob(chunk)
|
||||
JobQueue.shared.add(job)
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package org.session.libsession.messaging.utilities
|
||||
import android.content.Context
|
||||
import org.session.libsession.R
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.messaging.calls.CallMessageType
|
||||
import org.session.libsession.messaging.contacts.Contact
|
||||
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage
|
||||
import org.session.libsession.utilities.ExpirationUtil
|
||||
@@ -102,4 +103,19 @@ object UpdateMessageBuilder {
|
||||
context.getString(R.string.MessageRecord_media_saved_by_s, senderName)
|
||||
}
|
||||
}
|
||||
|
||||
fun buildCallMessage(context: Context, type: CallMessageType, sender: String): String {
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
val senderName = storage.getContactWithSessionID(sender)?.displayName(Contact.ContactContext.REGULAR) ?: sender
|
||||
return when (type) {
|
||||
CallMessageType.CALL_MISSED ->
|
||||
context.getString(R.string.MessageRecord_missed_call_from, senderName)
|
||||
CallMessageType.CALL_INCOMING ->
|
||||
context.getString(R.string.MessageRecord_s_called_you, senderName)
|
||||
CallMessageType.CALL_OUTGOING ->
|
||||
context.getString(R.string.MessageRecord_called_s, senderName)
|
||||
CallMessageType.CALL_FIRST_MISSED ->
|
||||
context.getString(R.string.MessageRecord_missed_call_from, senderName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,13 @@
|
||||
package org.session.libsession.messaging.utilities
|
||||
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import org.session.libsession.messaging.messages.control.CallMessage
|
||||
import java.util.*
|
||||
|
||||
object WebRtcUtils {
|
||||
|
||||
// TODO: move this to a better place that is persistent
|
||||
val SIGNAL_QUEUE = Channel<CallMessage>(Channel.UNLIMITED)
|
||||
val callCache: MutableMap<UUID, MutableSet<CallMessage>> = mutableMapOf()
|
||||
|
||||
}
|
@@ -12,6 +12,9 @@ import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import org.session.libsession.R
|
||||
import org.session.libsession.utilities.TextSecurePreferences.Companion.CALL_NOTIFICATIONS_ENABLED
|
||||
import org.session.libsession.utilities.TextSecurePreferences.Companion.SHOWN_CALL_NOTIFICATION
|
||||
import org.session.libsession.utilities.TextSecurePreferences.Companion.SHOWN_CALL_WARNING
|
||||
import org.session.libsignal.utilities.Log
|
||||
import java.io.IOException
|
||||
import java.util.Arrays
|
||||
@@ -153,6 +156,9 @@ interface TextSecurePreferences {
|
||||
fun setHasSeenLinkPreviewSuggestionDialog()
|
||||
fun hasHiddenMessageRequests(): Boolean
|
||||
fun setHasHiddenMessageRequests()
|
||||
fun setShownCallWarning(): Boolean
|
||||
fun setShownCallNotification(): Boolean
|
||||
fun isCallNotificationsEnabled(): Boolean
|
||||
fun clearAll()
|
||||
|
||||
companion object {
|
||||
@@ -230,6 +236,9 @@ interface TextSecurePreferences {
|
||||
const val LAST_PROFILE_UPDATE_TIME = "pref_last_profile_update_time"
|
||||
const val LAST_OPEN_DATE = "pref_last_open_date"
|
||||
const val HAS_HIDDEN_MESSAGE_REQUESTS = "pref_message_requests_hidden"
|
||||
const val CALL_NOTIFICATIONS_ENABLED = "pref_call_notifications_enabled"
|
||||
const val SHOWN_CALL_WARNING = "pref_shown_call_warning" // call warning is user-facing warning of enabling calls
|
||||
const val SHOWN_CALL_NOTIFICATION = "pref_shown_call_notification" // call notification is a promp to check privacy settings
|
||||
|
||||
@JvmStatic
|
||||
fun getLastConfigurationSyncTime(context: Context): Long {
|
||||
@@ -883,6 +892,22 @@ interface TextSecurePreferences {
|
||||
removePreference(context, HAS_HIDDEN_MESSAGE_REQUESTS)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isCallNotificationsEnabled(context: Context): Boolean {
|
||||
return getBooleanPreference(context, CALL_NOTIFICATIONS_ENABLED, false)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun setShownCallWarning(context: Context): Boolean {
|
||||
val previousValue = getBooleanPreference(context, SHOWN_CALL_WARNING, false)
|
||||
if (previousValue) {
|
||||
return false
|
||||
}
|
||||
val setValue = true
|
||||
setBooleanPreference(context, SHOWN_CALL_WARNING, setValue)
|
||||
return previousValue != setValue
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun clearAll(context: Context) {
|
||||
getDefaultSharedPreferences(context).edit().clear().commit()
|
||||
@@ -1439,6 +1464,33 @@ class AppTextSecurePreferences @Inject constructor(
|
||||
setBooleanPreference("has_seen_link_preview_suggestion_dialog", true)
|
||||
}
|
||||
|
||||
override fun isCallNotificationsEnabled(): Boolean {
|
||||
return getBooleanPreference(CALL_NOTIFICATIONS_ENABLED, false)
|
||||
}
|
||||
|
||||
override fun setShownCallNotification(): Boolean {
|
||||
val previousValue = getBooleanPreference(SHOWN_CALL_NOTIFICATION, false)
|
||||
if (previousValue) return false
|
||||
val setValue = true
|
||||
setBooleanPreference(SHOWN_CALL_NOTIFICATION, setValue)
|
||||
return previousValue != setValue
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the SHOWN_CALL_WARNING preference to `true`
|
||||
* Return `true` if the value did update (it was previously unset)
|
||||
*/
|
||||
override fun setShownCallWarning() : Boolean {
|
||||
val previousValue = getBooleanPreference(SHOWN_CALL_WARNING, false)
|
||||
if (previousValue) {
|
||||
return false
|
||||
}
|
||||
val setValue = true
|
||||
setBooleanPreference(SHOWN_CALL_WARNING, setValue)
|
||||
return previousValue != setValue
|
||||
}
|
||||
|
||||
override fun hasHiddenMessageRequests(): Boolean {
|
||||
return getBooleanPreference(TextSecurePreferences.HAS_HIDDEN_MESSAGE_REQUESTS, false)
|
||||
}
|
||||
@@ -1450,4 +1502,5 @@ class AppTextSecurePreferences @Inject constructor(
|
||||
override fun clearAll() {
|
||||
getDefaultSharedPreferences(context).edit().clear().commit()
|
||||
}
|
||||
|
||||
}
|
@@ -20,6 +20,7 @@
|
||||
<dimen name="small_profile_picture_size">36dp</dimen>
|
||||
<dimen name="medium_profile_picture_size">46dp</dimen>
|
||||
<dimen name="large_profile_picture_size">76dp</dimen>
|
||||
<dimen name="extra_large_profile_picture_size">128dp</dimen>
|
||||
<dimen name="conversation_view_status_indicator_size">14dp</dimen>
|
||||
<dimen name="border_thickness">1dp</dimen>
|
||||
<dimen name="new_conversation_button_collapsed_size">60dp</dimen>
|
||||
|
Reference in New Issue
Block a user