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:
Harris
2022-04-19 14:25:40 +10:00
committed by GitHub
parent 04dfe99517
commit e1b6bb7e56
115 changed files with 8054 additions and 286 deletions

View File

@@ -0,0 +1,7 @@
package org.session.libsession.database
interface CallDataProvider {
// answer/offer for call by UUID
// recipient info for call by UUID
}

View File

@@ -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
}

View File

@@ -0,0 +1,8 @@
package org.session.libsession.messaging.calls
enum class CallMessageType {
CALL_MISSED,
CALL_INCOMING,
CALL_OUTGOING,
CALL_FIRST_MISSED,
}

View File

@@ -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"

View File

@@ -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
}
}

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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!!)

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}
}
}

View File

@@ -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()
}

View File

@@ -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()
}
}

View File

@@ -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>