mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-25 02:55:23 +00:00
Merge remote-tracking branch 'upstream/dev' into libsession-integration
# Conflicts: # app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt # app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java # libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt # libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt
This commit is contained in:
commit
d5e4e6b7cd
@ -43,6 +43,7 @@ dependencies {
|
|||||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
|
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
|
||||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
|
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
|
||||||
implementation "androidx.lifecycle:lifecycle-process:$lifecycleVersion"
|
implementation "androidx.lifecycle:lifecycle-process:$lifecycleVersion"
|
||||||
|
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
|
||||||
implementation "androidx.paging:paging-runtime-ktx:$pagingVersion"
|
implementation "androidx.paging:paging-runtime-ktx:$pagingVersion"
|
||||||
implementation 'androidx.activity:activity-ktx:1.5.1'
|
implementation 'androidx.activity:activity-ktx:1.5.1'
|
||||||
implementation 'androidx.fragment:fragment-ktx:1.5.3'
|
implementation 'androidx.fragment:fragment-ktx:1.5.3'
|
||||||
|
@ -287,7 +287,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
|
|||||||
if (poller != null) {
|
if (poller != null) {
|
||||||
poller.stopIfNeeded();
|
poller.stopIfNeeded();
|
||||||
}
|
}
|
||||||
ClosedGroupPollerV2.getShared().stop();
|
ClosedGroupPollerV2.getShared().stopAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -467,11 +467,15 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
|
|||||||
String token = task.getResult().getToken();
|
String token = task.getResult().getToken();
|
||||||
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||||
if (userPublicKey == null) return Unit.INSTANCE;
|
if (userPublicKey == null) return Unit.INSTANCE;
|
||||||
|
|
||||||
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||||
if (TextSecurePreferences.isUsingFCM(this)) {
|
if (TextSecurePreferences.isUsingFCM(this)) {
|
||||||
LokiPushNotificationManager.register(token, userPublicKey, this, force);
|
LokiPushNotificationManager.register(token, userPublicKey, this, force);
|
||||||
} else {
|
} else {
|
||||||
LokiPushNotificationManager.unregister(token, this);
|
LokiPushNotificationManager.unregister(token, this);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@ import com.google.android.material.tabs.TabLayout;
|
|||||||
|
|
||||||
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
|
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||||
|
import org.session.libsession.snode.SnodeAPI;
|
||||||
import org.session.libsession.utilities.Address;
|
import org.session.libsession.utilities.Address;
|
||||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
|
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
|
||||||
import org.thoughtcrime.securesms.database.MediaDatabase;
|
import org.thoughtcrime.securesms.database.MediaDatabase;
|
||||||
@ -366,7 +367,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
|
|
||||||
private void sendMediaSavedNotificationIfNeeded() {
|
private void sendMediaSavedNotificationIfNeeded() {
|
||||||
if (recipient.isGroupRecipient()) return;
|
if (recipient.isGroupRecipient()) return;
|
||||||
DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis()));
|
DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(SnodeAPI.getNowWithOffset()));
|
||||||
MessageSender.send(message, recipient.getAddress());
|
MessageSender.send(message, recipient.getAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +60,7 @@ import androidx.viewpager.widget.ViewPager;
|
|||||||
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
|
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment;
|
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment;
|
||||||
|
import org.session.libsession.snode.SnodeAPI;
|
||||||
import org.session.libsession.utilities.Address;
|
import org.session.libsession.utilities.Address;
|
||||||
import org.session.libsession.utilities.Util;
|
import org.session.libsession.utilities.Util;
|
||||||
import org.session.libsession.utilities.recipients.Recipient;
|
import org.session.libsession.utilities.recipients.Recipient;
|
||||||
@ -423,7 +424,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
|||||||
.onAnyDenied(() -> Toast.makeText(this, R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show())
|
.onAnyDenied(() -> Toast.makeText(this, R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show())
|
||||||
.onAllGranted(() -> {
|
.onAllGranted(() -> {
|
||||||
SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this);
|
SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this);
|
||||||
long saveDate = (mediaItem.date > 0) ? mediaItem.date : System.currentTimeMillis();
|
long saveDate = (mediaItem.date > 0) ? mediaItem.date : SnodeAPI.getNowWithOffset();
|
||||||
saveTask.executeOnExecutor(
|
saveTask.executeOnExecutor(
|
||||||
AsyncTask.THREAD_POOL_EXECUTOR,
|
AsyncTask.THREAD_POOL_EXECUTOR,
|
||||||
new Attachment(mediaItem.uri, mediaItem.type, saveDate, null));
|
new Attachment(mediaItem.uri, mediaItem.type, saveDate, null));
|
||||||
@ -437,7 +438,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
|||||||
|
|
||||||
private void sendMediaSavedNotificationIfNeeded() {
|
private void sendMediaSavedNotificationIfNeeded() {
|
||||||
if (conversationRecipient.isGroupRecipient()) return;
|
if (conversationRecipient.isGroupRecipient()) return;
|
||||||
DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis()));
|
DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(SnodeAPI.getNowWithOffset()));
|
||||||
MessageSender.send(message, conversationRecipient.getAddress());
|
MessageSender.send(message, conversationRecipient.getAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import android.widget.TextView;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.session.libsession.snode.SnodeAPI;
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.conversation.v2.components.ExpirationTimerView;
|
import org.thoughtcrime.securesms.conversation.v2.components.ExpirationTimerView;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
@ -106,7 +107,7 @@ public class ConversationItemFooter extends LinearLayout {
|
|||||||
messageRecord.getExpiresIn());
|
messageRecord.getExpiresIn());
|
||||||
this.timerView.startAnimation();
|
this.timerView.startAnimation();
|
||||||
|
|
||||||
if (messageRecord.getExpireStarted() + messageRecord.getExpiresIn() <= System.currentTimeMillis()) {
|
if (messageRecord.getExpireStarted() + messageRecord.getExpiresIn() <= SnodeAPI.getNowWithOffset()) {
|
||||||
ApplicationContext.getInstance(getContext()).getExpiringMessageManager().checkSchedule();
|
ApplicationContext.getInstance(getContext()).getExpiringMessageManager().checkSchedule();
|
||||||
}
|
}
|
||||||
} else if (!messageRecord.isOutgoing() && !messageRecord.isMediaPending()) {
|
} else if (!messageRecord.isOutgoing() && !messageRecord.isMediaPending()) {
|
||||||
|
@ -166,6 +166,7 @@ import org.thoughtcrime.securesms.util.MediaUtil
|
|||||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask
|
import org.thoughtcrime.securesms.util.SaveAttachmentTask
|
||||||
import org.thoughtcrime.securesms.util.push
|
import org.thoughtcrime.securesms.util.push
|
||||||
import org.thoughtcrime.securesms.util.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
import java.util.concurrent.atomic.AtomicLong
|
import java.util.concurrent.atomic.AtomicLong
|
||||||
@ -364,12 +365,13 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
// messageIdToScroll
|
// messageIdToScroll
|
||||||
messageToScrollTimestamp.set(intent.getLongExtra(SCROLL_MESSAGE_ID, -1))
|
messageToScrollTimestamp.set(intent.getLongExtra(SCROLL_MESSAGE_ID, -1))
|
||||||
messageToScrollAuthor.set(intent.getParcelableExtra(SCROLL_MESSAGE_AUTHOR))
|
messageToScrollAuthor.set(intent.getParcelableExtra(SCROLL_MESSAGE_AUTHOR))
|
||||||
val thread = threadDb.getRecipientForThreadId(viewModel.threadId)
|
val recipient = viewModel.recipient
|
||||||
if (thread == null) {
|
val openGroup = recipient.let { viewModel.openGroup }
|
||||||
|
if (recipient == null || (recipient.isOpenGroupRecipient && openGroup == null)) {
|
||||||
Toast.makeText(this, "This thread has been deleted.", Toast.LENGTH_LONG).show()
|
Toast.makeText(this, "This thread has been deleted.", Toast.LENGTH_LONG).show()
|
||||||
return finish()
|
return finish()
|
||||||
}
|
}
|
||||||
setUpRecyclerView()
|
|
||||||
setUpToolBar()
|
setUpToolBar()
|
||||||
setUpInputBar()
|
setUpInputBar()
|
||||||
setUpLinkPreviewObserver()
|
setUpLinkPreviewObserver()
|
||||||
@ -396,22 +398,31 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unreadCount = mmsSmsDb.getUnreadCount(viewModel.threadId)
|
|
||||||
updateUnreadCountIndicator()
|
updateUnreadCountIndicator()
|
||||||
setUpTypingObserver()
|
|
||||||
setUpRecipientObserver()
|
|
||||||
updateSubtitle()
|
updateSubtitle()
|
||||||
getLatestOpenGroupInfoIfNeeded()
|
|
||||||
setUpBlockedBanner()
|
setUpBlockedBanner()
|
||||||
binding!!.searchBottomBar.setEventListener(this)
|
binding!!.searchBottomBar.setEventListener(this)
|
||||||
setUpSearchResultObserver()
|
|
||||||
scrollToFirstUnreadMessageIfNeeded()
|
|
||||||
showOrHideInputIfNeeded()
|
showOrHideInputIfNeeded()
|
||||||
setUpMessageRequestsBar()
|
setUpMessageRequestsBar()
|
||||||
viewModel.recipient?.let { recipient ->
|
|
||||||
if (recipient.isOpenGroupRecipient && viewModel.openGroup == null) {
|
val weakActivity = WeakReference(this)
|
||||||
Toast.makeText(this, "This thread has been deleted.", Toast.LENGTH_LONG).show()
|
|
||||||
return finish()
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
unreadCount = mmsSmsDb.getUnreadCount(viewModel.threadId)
|
||||||
|
|
||||||
|
// Note: We are accessing the `adapter` property because we want it to be loaded on
|
||||||
|
// the background thread to avoid blocking the UI thread and potentially hanging when
|
||||||
|
// transitioning to the activity
|
||||||
|
weakActivity.get()?.adapter ?: return@launch
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
setUpRecyclerView()
|
||||||
|
setUpTypingObserver()
|
||||||
|
setUpRecipientObserver()
|
||||||
|
getLatestOpenGroupInfoIfNeeded()
|
||||||
|
setUpSearchResultObserver()
|
||||||
|
scrollToFirstUnreadMessageIfNeeded()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -709,6 +720,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
|
|
||||||
// region Animation & Updating
|
// region Animation & Updating
|
||||||
override fun onModified(recipient: Recipient) {
|
override fun onModified(recipient: Recipient) {
|
||||||
|
viewModel.updateRecipient()
|
||||||
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
val threadRecipient = viewModel.recipient ?: return@runOnUiThread Log.d("Loki-DBG", "Recipient no longer here, go back?")
|
val threadRecipient = viewModel.recipient ?: return@runOnUiThread Log.d("Loki-DBG", "Recipient no longer here, go back?")
|
||||||
if (threadRecipient.isContactRecipient) {
|
if (threadRecipient.isContactRecipient) {
|
||||||
@ -1063,7 +1076,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
storage.setExpirationTimer(thread.address.serialize(), expirationTime)
|
storage.setExpirationTimer(thread.address.serialize(), expirationTime)
|
||||||
val message = ExpirationTimerUpdate(expirationTime)
|
val message = ExpirationTimerUpdate(expirationTime)
|
||||||
message.recipient = thread.address.serialize()
|
message.recipient = thread.address.serialize()
|
||||||
message.sentTimestamp = System.currentTimeMillis()
|
message.sentTimestamp = SnodeAPI.nowWithOffset
|
||||||
val expiringMessageManager = ApplicationContext.getInstance(this).expiringMessageManager
|
val expiringMessageManager = ApplicationContext.getInstance(this).expiringMessageManager
|
||||||
expiringMessageManager.setExpirationTimer(message)
|
expiringMessageManager.setExpirationTimer(message)
|
||||||
MessageSender.send(message, thread.address)
|
MessageSender.send(message, thread.address)
|
||||||
@ -1192,7 +1205,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
// Create the message
|
// Create the message
|
||||||
val recipient = viewModel.recipient ?: return
|
val recipient = viewModel.recipient ?: return
|
||||||
val reactionMessage = VisibleMessage()
|
val reactionMessage = VisibleMessage()
|
||||||
val emojiTimestamp = System.currentTimeMillis()
|
val emojiTimestamp = SnodeAPI.nowWithOffset
|
||||||
reactionMessage.sentTimestamp = emojiTimestamp
|
reactionMessage.sentTimestamp = emojiTimestamp
|
||||||
val author = textSecurePreferences.getLocalNumber()!!
|
val author = textSecurePreferences.getLocalNumber()!!
|
||||||
// Put the message in the database
|
// Put the message in the database
|
||||||
@ -1225,7 +1238,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
private fun sendEmojiRemoval(emoji: String, originalMessage: MessageRecord) {
|
private fun sendEmojiRemoval(emoji: String, originalMessage: MessageRecord) {
|
||||||
val recipient = viewModel.recipient ?: return
|
val recipient = viewModel.recipient ?: return
|
||||||
val message = VisibleMessage()
|
val message = VisibleMessage()
|
||||||
val emojiTimestamp = System.currentTimeMillis()
|
val emojiTimestamp = SnodeAPI.nowWithOffset
|
||||||
message.sentTimestamp = emojiTimestamp
|
message.sentTimestamp = emojiTimestamp
|
||||||
val author = textSecurePreferences.getLocalNumber()!!
|
val author = textSecurePreferences.getLocalNumber()!!
|
||||||
reactionDb.deleteReaction(emoji, MessageId(originalMessage.id, originalMessage.isMms), author, false)
|
reactionDb.deleteReaction(emoji, MessageId(originalMessage.id, originalMessage.isMms), author, false)
|
||||||
@ -1454,7 +1467,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
}
|
}
|
||||||
// Create the message
|
// Create the message
|
||||||
val message = VisibleMessage()
|
val message = VisibleMessage()
|
||||||
message.sentTimestamp = System.currentTimeMillis()
|
message.sentTimestamp = SnodeAPI.nowWithOffset
|
||||||
message.text = text
|
message.text = text
|
||||||
val outgoingTextMessage = OutgoingTextMessage.from(message, recipient)
|
val outgoingTextMessage = OutgoingTextMessage.from(message, recipient)
|
||||||
// Clear the input bar
|
// Clear the input bar
|
||||||
@ -1478,7 +1491,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
processMessageRequestApproval()
|
processMessageRequestApproval()
|
||||||
// Create the message
|
// Create the message
|
||||||
val message = VisibleMessage()
|
val message = VisibleMessage()
|
||||||
message.sentTimestamp = System.currentTimeMillis()
|
message.sentTimestamp = SnodeAPI.nowWithOffset
|
||||||
message.text = body
|
message.text = body
|
||||||
val quote = quotedMessage?.let {
|
val quote = quotedMessage?.let {
|
||||||
val quotedAttachments = (it as? MmsMessageRecord)?.slideDeck?.asAttachments() ?: listOf()
|
val quotedAttachments = (it as? MmsMessageRecord)?.slideDeck?.asAttachments() ?: listOf()
|
||||||
@ -1875,7 +1888,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
private fun sendMediaSavedNotification() {
|
private fun sendMediaSavedNotification() {
|
||||||
val recipient = viewModel.recipient ?: return
|
val recipient = viewModel.recipient ?: return
|
||||||
if (recipient.isGroupRecipient) { return }
|
if (recipient.isGroupRecipient) { return }
|
||||||
val timestamp = System.currentTimeMillis()
|
val timestamp = SnodeAPI.nowWithOffset
|
||||||
val kind = DataExtractionNotification.Kind.MediaSaved(timestamp)
|
val kind = DataExtractionNotification.Kind.MediaSaved(timestamp)
|
||||||
val message = DataExtractionNotification(kind)
|
val message = DataExtractionNotification(kind)
|
||||||
MessageSender.send(message, recipient.address)
|
MessageSender.send(message, recipient.address)
|
||||||
|
@ -38,11 +38,17 @@ class ConversationViewModel(
|
|||||||
private val _uiState = MutableStateFlow(ConversationUiState(conversationExists = recipient != null))
|
private val _uiState = MutableStateFlow(ConversationUiState(conversationExists = recipient != null))
|
||||||
val uiState: StateFlow<ConversationUiState> = _uiState
|
val uiState: StateFlow<ConversationUiState> = _uiState
|
||||||
|
|
||||||
|
private var _recipient: RetrieveOnce<Recipient> = RetrieveOnce {
|
||||||
|
repository.maybeGetRecipientForThreadId(threadId)
|
||||||
|
}
|
||||||
val recipient: Recipient?
|
val recipient: Recipient?
|
||||||
get() = repository.maybeGetRecipientForThreadId(threadId)
|
get() = _recipient.value
|
||||||
|
|
||||||
|
private var _openGroup: RetrieveOnce<OpenGroup> = RetrieveOnce {
|
||||||
|
storage.getOpenGroup(threadId)
|
||||||
|
}
|
||||||
val openGroup: OpenGroup?
|
val openGroup: OpenGroup?
|
||||||
get() = storage.getOpenGroup(threadId)
|
get() = _openGroup.value
|
||||||
|
|
||||||
val serverCapabilities: List<String>
|
val serverCapabilities: List<String>
|
||||||
get() = openGroup?.let { storage.getServerCapabilities(it.server) } ?: listOf()
|
get() = openGroup?.let { storage.getServerCapabilities(it.server) } ?: listOf()
|
||||||
@ -186,6 +192,10 @@ class ConversationViewModel(
|
|||||||
return repository.hasReceived(threadId)
|
return repository.hasReceived(threadId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateRecipient() {
|
||||||
|
_recipient.updateTo(repository.maybeGetRecipientForThreadId(threadId))
|
||||||
|
}
|
||||||
|
|
||||||
@dagger.assisted.AssistedFactory
|
@dagger.assisted.AssistedFactory
|
||||||
interface AssistedFactory {
|
interface AssistedFactory {
|
||||||
fun create(threadId: Long, edKeyPair: KeyPair?, contentResolver: ContentResolver): Factory
|
fun create(threadId: Long, edKeyPair: KeyPair?, contentResolver: ContentResolver): Factory
|
||||||
@ -213,3 +223,19 @@ data class ConversationUiState(
|
|||||||
val isMessageRequestAccepted: Boolean? = null,
|
val isMessageRequestAccepted: Boolean? = null,
|
||||||
val conversationExists: Boolean
|
val conversationExists: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class RetrieveOnce<T>(val retrieval: () -> T?) {
|
||||||
|
private var triedToRetrieve: Boolean = false
|
||||||
|
private var _value: T? = null
|
||||||
|
|
||||||
|
val value: T?
|
||||||
|
get() {
|
||||||
|
if (triedToRetrieve) { return _value }
|
||||||
|
|
||||||
|
triedToRetrieve = true
|
||||||
|
_value = retrieval()
|
||||||
|
return _value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateTo(value: T?) { _value = value }
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@ import org.session.libsession.messaging.MessagingModuleConfiguration
|
|||||||
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.session.libsession.messaging.utilities.SessionId
|
import org.session.libsession.messaging.utilities.SessionId
|
||||||
import org.session.libsession.messaging.utilities.SodiumUtilities
|
import org.session.libsession.messaging.utilities.SodiumUtilities
|
||||||
|
import org.session.libsession.snode.SnodeAPI
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.session.libsession.utilities.ExpirationUtil
|
import org.session.libsession.utilities.ExpirationUtil
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
@ -88,7 +89,7 @@ class MessageDetailActivity: PassphraseRequiredActionBarActivity() {
|
|||||||
binding.expiresContainer.visibility = View.GONE
|
binding.expiresContainer.visibility = View.GONE
|
||||||
} else {
|
} else {
|
||||||
binding.expiresContainer.visibility = View.VISIBLE
|
binding.expiresContainer.visibility = View.VISIBLE
|
||||||
val elapsed = System.currentTimeMillis() - messageRecord!!.expireStarted
|
val elapsed = SnodeAPI.nowWithOffset - messageRecord!!.expireStarted
|
||||||
val remaining = messageRecord!!.expiresIn - elapsed
|
val remaining = messageRecord!!.expiresIn - elapsed
|
||||||
|
|
||||||
val duration = ExpirationUtil.getExpirationDisplayValue(this, Math.max((remaining / 1000).toInt(), 1))
|
val duration = ExpirationUtil.getExpirationDisplayValue(this, Math.max((remaining / 1000).toInt(), 1))
|
||||||
|
@ -26,6 +26,7 @@ import network.loki.messenger.databinding.ViewVisibleMessageBinding
|
|||||||
import org.session.libsession.messaging.contacts.Contact
|
import org.session.libsession.messaging.contacts.Contact
|
||||||
import org.session.libsession.messaging.contacts.Contact.ContactContext
|
import org.session.libsession.messaging.contacts.Contact.ContactContext
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
|
import org.session.libsession.snode.SnodeAPI
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.session.libsession.utilities.ViewUtil
|
import org.session.libsession.utilities.ViewUtil
|
||||||
import org.session.libsession.utilities.getColorFromAttr
|
import org.session.libsession.utilities.getColorFromAttr
|
||||||
@ -315,7 +316,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
if (message.expireStarted > 0) {
|
if (message.expireStarted > 0) {
|
||||||
binding.expirationTimerView.setExpirationTime(message.expireStarted, message.expiresIn)
|
binding.expirationTimerView.setExpirationTime(message.expireStarted, message.expiresIn)
|
||||||
binding.expirationTimerView.startAnimation()
|
binding.expirationTimerView.startAnimation()
|
||||||
if (message.expireStarted + message.expiresIn <= System.currentTimeMillis()) {
|
if (message.expireStarted + message.expiresIn <= SnodeAPI.nowWithOffset) {
|
||||||
ApplicationContext.getInstance(context).expiringMessageManager.checkSchedule()
|
ApplicationContext.getInstance(context).expiringMessageManager.checkSchedule()
|
||||||
}
|
}
|
||||||
} else if (!message.isMediaPending) {
|
} else if (!message.isMediaPending) {
|
||||||
|
@ -33,6 +33,7 @@ import org.session.libsession.messaging.sending_receiving.attachments.Attachment
|
|||||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
|
||||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
|
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
|
||||||
|
import org.session.libsession.snode.SnodeAPI
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.session.libsession.utilities.Address.Companion.UNKNOWN
|
import org.session.libsession.utilities.Address.Companion.UNKNOWN
|
||||||
import org.session.libsession.utilities.Address.Companion.fromExternal
|
import org.session.libsession.utilities.Address.Companion.fromExternal
|
||||||
@ -340,7 +341,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun markExpireStarted(messageId: Long) {
|
override fun markExpireStarted(messageId: Long) {
|
||||||
markExpireStarted(messageId, System.currentTimeMillis())
|
markExpireStarted(messageId, SnodeAPI.nowWithOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun markExpireStarted(messageId: Long, startedTimestamp: Long) {
|
override fun markExpireStarted(messageId: Long, startedTimestamp: Long) {
|
||||||
@ -731,7 +732,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
|||||||
// In open groups messages should be sorted by their server timestamp
|
// In open groups messages should be sorted by their server timestamp
|
||||||
var receivedTimestamp = serverTimestamp
|
var receivedTimestamp = serverTimestamp
|
||||||
if (serverTimestamp == 0L) {
|
if (serverTimestamp == 0L) {
|
||||||
receivedTimestamp = System.currentTimeMillis()
|
receivedTimestamp = SnodeAPI.nowWithOffset
|
||||||
}
|
}
|
||||||
contentValues.put(DATE_RECEIVED, receivedTimestamp)
|
contentValues.put(DATE_RECEIVED, receivedTimestamp)
|
||||||
contentValues.put(SUBSCRIPTION_ID, message.subscriptionId)
|
contentValues.put(SUBSCRIPTION_ID, message.subscriptionId)
|
||||||
@ -1210,7 +1211,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
|||||||
val slideDeck = SlideDeck(context, message!!.attachments)
|
val slideDeck = SlideDeck(context, message!!.attachments)
|
||||||
return MediaMmsMessageRecord(
|
return MediaMmsMessageRecord(
|
||||||
id, message.recipient, message.recipient,
|
id, message.recipient, message.recipient,
|
||||||
1, System.currentTimeMillis(), System.currentTimeMillis(),
|
1, SnodeAPI.nowWithOffset, SnodeAPI.nowWithOffset,
|
||||||
0, threadId, message.body,
|
0, threadId, message.body,
|
||||||
slideDeck, slideDeck.slides.size,
|
slideDeck, slideDeck.slides.size,
|
||||||
if (message.isSecure) MmsSmsColumns.Types.getOutgoingEncryptedMessageType() else MmsSmsColumns.Types.getOutgoingSmsMessageType(),
|
if (message.isSecure) MmsSmsColumns.Types.getOutgoingEncryptedMessageType() else MmsSmsColumns.Types.getOutgoingSmsMessageType(),
|
||||||
@ -1218,7 +1219,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
|||||||
LinkedList(),
|
LinkedList(),
|
||||||
message.subscriptionId,
|
message.subscriptionId,
|
||||||
message.expiresIn,
|
message.expiresIn,
|
||||||
System.currentTimeMillis(), 0,
|
SnodeAPI.nowWithOffset, 0,
|
||||||
if (message.outgoingQuote != null) Quote(
|
if (message.outgoingQuote != null) Quote(
|
||||||
message.outgoingQuote!!.id,
|
message.outgoingQuote!!.id,
|
||||||
message.outgoingQuote!!.author,
|
message.outgoingQuote!!.author,
|
||||||
|
@ -36,6 +36,7 @@ import org.session.libsession.messaging.calls.CallMessageType;
|
|||||||
import org.session.libsession.messaging.messages.signal.IncomingGroupMessage;
|
import org.session.libsession.messaging.messages.signal.IncomingGroupMessage;
|
||||||
import org.session.libsession.messaging.messages.signal.IncomingTextMessage;
|
import org.session.libsession.messaging.messages.signal.IncomingTextMessage;
|
||||||
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
|
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
|
||||||
|
import org.session.libsession.snode.SnodeAPI;
|
||||||
import org.session.libsession.utilities.Address;
|
import org.session.libsession.utilities.Address;
|
||||||
import org.session.libsession.utilities.IdentityKeyMismatch;
|
import org.session.libsession.utilities.IdentityKeyMismatch;
|
||||||
import org.session.libsession.utilities.IdentityKeyMismatchList;
|
import org.session.libsession.utilities.IdentityKeyMismatchList;
|
||||||
@ -223,7 +224,7 @@ public class SmsDatabase extends MessagingDatabase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void markExpireStarted(long id) {
|
public void markExpireStarted(long id) {
|
||||||
markExpireStarted(id, System.currentTimeMillis());
|
markExpireStarted(id, SnodeAPI.getNowWithOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -525,7 +526,7 @@ public class SmsDatabase extends MessagingDatabase {
|
|||||||
contentValues.put(ADDRESS, address.serialize());
|
contentValues.put(ADDRESS, address.serialize());
|
||||||
contentValues.put(THREAD_ID, threadId);
|
contentValues.put(THREAD_ID, threadId);
|
||||||
contentValues.put(BODY, message.getMessageBody());
|
contentValues.put(BODY, message.getMessageBody());
|
||||||
contentValues.put(DATE_RECEIVED, System.currentTimeMillis());
|
contentValues.put(DATE_RECEIVED, SnodeAPI.getNowWithOffset());
|
||||||
contentValues.put(DATE_SENT, message.getSentTimestampMillis());
|
contentValues.put(DATE_SENT, message.getSentTimestampMillis());
|
||||||
contentValues.put(READ, 1);
|
contentValues.put(READ, 1);
|
||||||
contentValues.put(TYPE, type);
|
contentValues.put(TYPE, type);
|
||||||
@ -760,11 +761,11 @@ public class SmsDatabase extends MessagingDatabase {
|
|||||||
public MessageRecord getCurrent() {
|
public MessageRecord getCurrent() {
|
||||||
return new SmsMessageRecord(id, message.getMessageBody(),
|
return new SmsMessageRecord(id, message.getMessageBody(),
|
||||||
message.getRecipient(), message.getRecipient(),
|
message.getRecipient(), message.getRecipient(),
|
||||||
System.currentTimeMillis(), System.currentTimeMillis(),
|
SnodeAPI.getNowWithOffset(), SnodeAPI.getNowWithOffset(),
|
||||||
0, message.isSecureMessage() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(),
|
0, message.isSecureMessage() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(),
|
||||||
threadId, 0, new LinkedList<IdentityKeyMismatch>(),
|
threadId, 0, new LinkedList<IdentityKeyMismatch>(),
|
||||||
message.getExpiresIn(),
|
message.getExpiresIn(),
|
||||||
System.currentTimeMillis(), 0, false, Collections.emptyList(), false);
|
SnodeAPI.getNowWithOffset(), 0, false, Collections.emptyList(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ public class ThreadDatabase extends Database {
|
|||||||
|
|
||||||
private long createThreadForRecipient(Address address, boolean group, int distributionType) {
|
private long createThreadForRecipient(Address address, boolean group, int distributionType) {
|
||||||
ContentValues contentValues = new ContentValues(4);
|
ContentValues contentValues = new ContentValues(4);
|
||||||
long date = System.currentTimeMillis();
|
long date = SnodeAPI.getNowWithOffset();
|
||||||
|
|
||||||
contentValues.put(DATE, date - date % 1000);
|
contentValues.put(DATE, date - date % 1000);
|
||||||
contentValues.put(ADDRESS, address.serialize());
|
contentValues.put(ADDRESS, address.serialize());
|
||||||
@ -335,7 +335,7 @@ public class ThreadDatabase extends Database {
|
|||||||
contentValues.put(UNREAD_MENTION_COUNT, 0);
|
contentValues.put(UNREAD_MENTION_COUNT, 0);
|
||||||
|
|
||||||
if (lastSeen) {
|
if (lastSeen) {
|
||||||
contentValues.put(LAST_SEEN, System.currentTimeMillis());
|
contentValues.put(LAST_SEEN, SnodeAPI.getNowWithOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||||
|
@ -30,6 +30,7 @@ import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
|
|||||||
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
|
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
|
||||||
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||||
|
import org.session.libsession.snode.SnodeAPI;
|
||||||
import org.session.libsession.utilities.Address;
|
import org.session.libsession.utilities.Address;
|
||||||
import org.session.libsession.utilities.recipients.Recipient;
|
import org.session.libsession.utilities.recipients.Recipient;
|
||||||
import org.session.libsignal.utilities.Log;
|
import org.session.libsignal.utilities.Log;
|
||||||
@ -82,7 +83,7 @@ public class AndroidAutoReplyReceiver extends BroadcastReceiver {
|
|||||||
|
|
||||||
VisibleMessage message = new VisibleMessage();
|
VisibleMessage message = new VisibleMessage();
|
||||||
message.setText(responseText.toString());
|
message.setText(responseText.toString());
|
||||||
message.setSentTimestamp(System.currentTimeMillis());
|
message.setSentTimestamp(SnodeAPI.getNowWithOffset());
|
||||||
MessageSender.send(message, recipient.getAddress());
|
MessageSender.send(message, recipient.getAddress());
|
||||||
|
|
||||||
if (recipient.isGroupRecipient()) {
|
if (recipient.isGroupRecipient()) {
|
||||||
@ -96,7 +97,7 @@ public class AndroidAutoReplyReceiver extends BroadcastReceiver {
|
|||||||
} else {
|
} else {
|
||||||
Log.w("AndroidAutoReplyReceiver", "Sending regular message ");
|
Log.w("AndroidAutoReplyReceiver", "Sending regular message ");
|
||||||
OutgoingTextMessage reply = OutgoingTextMessage.from(message, recipient);
|
OutgoingTextMessage reply = OutgoingTextMessage.from(message, recipient);
|
||||||
DatabaseComponent.get(context).smsDatabase().insertMessageOutbox(replyThreadId, reply, false, System.currentTimeMillis(), null, true);
|
DatabaseComponent.get(context).smsDatabase().insertMessageOutbox(replyThreadId, reply, false, SnodeAPI.getNowWithOffset(), null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<MarkedMessageInfo> messageIds = DatabaseComponent.get(context).threadDatabase().setRead(replyThreadId, true);
|
List<MarkedMessageInfo> messageIds = DatabaseComponent.get(context).threadDatabase().setRead(replyThreadId, true);
|
||||||
|
@ -4,9 +4,11 @@ import android.content.BroadcastReceiver
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.work.Constraints
|
import androidx.work.Constraints
|
||||||
|
import androidx.work.Data
|
||||||
import androidx.work.ExistingPeriodicWorkPolicy
|
import androidx.work.ExistingPeriodicWorkPolicy
|
||||||
import androidx.work.NetworkType
|
import androidx.work.NetworkType
|
||||||
import androidx.work.PeriodicWorkRequestBuilder
|
import androidx.work.PeriodicWorkRequestBuilder
|
||||||
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import androidx.work.Worker
|
import androidx.work.Worker
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
@ -21,19 +23,35 @@ import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPolle
|
|||||||
import org.session.libsession.snode.SnodeAPI
|
import org.session.libsession.snode.SnodeAPI
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
|
import org.session.libsignal.utilities.recover
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Worker(context, params) {
|
class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||||
|
enum class Targets {
|
||||||
|
DMS, CLOSED_GROUPS, OPEN_GROUPS
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "BackgroundPollWorker"
|
const val TAG = "BackgroundPollWorker"
|
||||||
|
const val INITIAL_SCHEDULE_TIME = "INITIAL_SCHEDULE_TIME"
|
||||||
|
const val REQUEST_TARGETS = "REQUEST_TARGETS"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun schedulePeriodic(context: Context) {
|
fun schedulePeriodic(context: Context) = schedulePeriodic(context, targets = Targets.values())
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun schedulePeriodic(context: Context, targets: Array<Targets>) {
|
||||||
Log.v(TAG, "Scheduling periodic work.")
|
Log.v(TAG, "Scheduling periodic work.")
|
||||||
val builder = PeriodicWorkRequestBuilder<BackgroundPollWorker>(15, TimeUnit.MINUTES)
|
val durationMinutes: Long = 15
|
||||||
|
val builder = PeriodicWorkRequestBuilder<BackgroundPollWorker>(durationMinutes, TimeUnit.MINUTES)
|
||||||
builder.setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build())
|
builder.setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build())
|
||||||
|
|
||||||
|
val dataBuilder = Data.Builder()
|
||||||
|
dataBuilder.putLong(INITIAL_SCHEDULE_TIME, System.currentTimeMillis() + (durationMinutes * 60 * 1000))
|
||||||
|
dataBuilder.putStringArray(REQUEST_TARGETS, targets.map { it.name }.toTypedArray())
|
||||||
|
builder.setInputData(dataBuilder.build())
|
||||||
|
|
||||||
val workRequest = builder.build()
|
val workRequest = builder.build()
|
||||||
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
|
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
|
||||||
TAG,
|
TAG,
|
||||||
@ -41,6 +59,20 @@ class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Wor
|
|||||||
workRequest
|
workRequest
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun scheduleOnce(context: Context, targets: Array<Targets> = Targets.values()) {
|
||||||
|
Log.v(TAG, "Scheduling single run.")
|
||||||
|
val builder = OneTimeWorkRequestBuilder<BackgroundPollWorker>()
|
||||||
|
builder.setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build())
|
||||||
|
|
||||||
|
val dataBuilder = Data.Builder()
|
||||||
|
dataBuilder.putStringArray(REQUEST_TARGETS, targets.map { it.name }.toTypedArray())
|
||||||
|
builder.setInputData(dataBuilder.build())
|
||||||
|
|
||||||
|
val workRequest = builder.build()
|
||||||
|
WorkManager.getInstance(context).enqueue(workRequest)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun doWork(): Result {
|
override fun doWork(): Result {
|
||||||
@ -49,13 +81,35 @@ class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Wor
|
|||||||
return Result.failure()
|
return Result.failure()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is a scheduled run and it is happening before the initial scheduled time (as
|
||||||
|
// periodic background tasks run immediately when scheduled) then don't actually do anything
|
||||||
|
// because this might slow requests on initial startup or triggered by PNs
|
||||||
|
val initialScheduleTime = inputData.getLong(INITIAL_SCHEDULE_TIME, -1)
|
||||||
|
|
||||||
|
if (initialScheduleTime != -1L && System.currentTimeMillis() < (initialScheduleTime - (60 * 1000))) {
|
||||||
|
Log.v(TAG, "Skipping initial run.")
|
||||||
|
return Result.success()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the desired targets (defaulting to all if not provided or empty)
|
||||||
|
val requestTargets: List<Targets> = (inputData.getStringArray(REQUEST_TARGETS) ?: emptyArray())
|
||||||
|
.map {
|
||||||
|
try { Targets.valueOf(it) }
|
||||||
|
catch(e: Exception) { null }
|
||||||
|
}
|
||||||
|
.filterNotNull()
|
||||||
|
.ifEmpty { Targets.values().toList() }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Log.v(TAG, "Performing background poll.")
|
Log.v(TAG, "Performing background poll for ${requestTargets.joinToString { it.name }}.")
|
||||||
val promises = mutableListOf<Promise<Unit, Exception>>()
|
val promises = mutableListOf<Promise<Unit, Exception>>()
|
||||||
|
|
||||||
// DMs
|
// DMs
|
||||||
|
var dmsPromise: Promise<Unit, Exception> = Promise.ofSuccess(Unit)
|
||||||
|
|
||||||
|
if (requestTargets.contains(Targets.DMS)) {
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
||||||
val dmsPromise = SnodeAPI.getMessages(userPublicKey).bind { envelopes ->
|
dmsPromise = SnodeAPI.getMessages(userPublicKey).bind { envelopes ->
|
||||||
val params = envelopes.map { (envelope, serverHash) ->
|
val params = envelopes.map { (envelope, serverHash) ->
|
||||||
// FIXME: Using a job here seems like a bad idea...
|
// FIXME: Using a job here seems like a bad idea...
|
||||||
MessageReceiveParameters(envelope.toByteArray(), serverHash, null)
|
MessageReceiveParameters(envelope.toByteArray(), serverHash, null)
|
||||||
@ -63,14 +117,20 @@ class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Wor
|
|||||||
BatchMessageReceiveJob(params).executeAsync("background")
|
BatchMessageReceiveJob(params).executeAsync("background")
|
||||||
}
|
}
|
||||||
promises.add(dmsPromise)
|
promises.add(dmsPromise)
|
||||||
|
}
|
||||||
|
|
||||||
// Closed groups
|
// Closed groups
|
||||||
|
if (requestTargets.contains(Targets.CLOSED_GROUPS)) {
|
||||||
val closedGroupPoller = ClosedGroupPollerV2() // Intentionally don't use shared
|
val closedGroupPoller = ClosedGroupPollerV2() // Intentionally don't use shared
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
val allGroupPublicKeys = storage.getAllClosedGroupPublicKeys()
|
val allGroupPublicKeys = storage.getAllClosedGroupPublicKeys()
|
||||||
allGroupPublicKeys.iterator().forEach { closedGroupPoller.poll(it) }
|
allGroupPublicKeys.iterator().forEach { closedGroupPoller.poll(it) }
|
||||||
|
}
|
||||||
|
|
||||||
// Open Groups
|
// Open Groups
|
||||||
|
var ogPollError: Exception? = null
|
||||||
|
|
||||||
|
if (requestTargets.contains(Targets.OPEN_GROUPS)) {
|
||||||
val threadDB = DatabaseComponent.get(context).lokiThreadDatabase()
|
val threadDB = DatabaseComponent.get(context).lokiThreadDatabase()
|
||||||
val openGroups = threadDB.getAllOpenGroups()
|
val openGroups = threadDB.getAllOpenGroups()
|
||||||
val openGroupServers = openGroups.map { it.value.server }.toSet()
|
val openGroupServers = openGroups.map { it.value.server }.toSet()
|
||||||
@ -78,12 +138,32 @@ class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Wor
|
|||||||
for (server in openGroupServers) {
|
for (server in openGroupServers) {
|
||||||
val poller = OpenGroupPoller(server, null)
|
val poller = OpenGroupPoller(server, null)
|
||||||
poller.hasStarted = true
|
poller.hasStarted = true
|
||||||
promises.add(poller.poll())
|
|
||||||
|
// If one of the open group pollers fails we don't want it to cancel the DM
|
||||||
|
// poller so just hold on to the error for later
|
||||||
|
promises.add(
|
||||||
|
poller.poll().recover {
|
||||||
|
if (dmsPromise.isDone()) {
|
||||||
|
throw it
|
||||||
|
}
|
||||||
|
|
||||||
|
ogPollError = it
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait until all the promises are resolved
|
// Wait until all the promises are resolved
|
||||||
all(promises).get()
|
all(promises).get()
|
||||||
|
|
||||||
|
// If the Open Group pollers threw an exception then re-throw it here (now that
|
||||||
|
// the DM promise has completed)
|
||||||
|
val localOgPollException = ogPollError
|
||||||
|
|
||||||
|
if (localOgPollException != null) {
|
||||||
|
throw localOgPollException
|
||||||
|
}
|
||||||
|
|
||||||
return Result.success()
|
return Result.success()
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.e(TAG, "Background poll failed due to error: ${exception.message}.", exception)
|
Log.e(TAG, "Background poll failed due to error: ${exception.message}.", exception)
|
||||||
|
@ -44,6 +44,7 @@ import org.session.libsession.messaging.open_groups.OpenGroup;
|
|||||||
import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier;
|
import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier;
|
||||||
import org.session.libsession.messaging.utilities.SessionId;
|
import org.session.libsession.messaging.utilities.SessionId;
|
||||||
import org.session.libsession.messaging.utilities.SodiumUtilities;
|
import org.session.libsession.messaging.utilities.SodiumUtilities;
|
||||||
|
import org.session.libsession.snode.SnodeAPI;
|
||||||
import org.session.libsession.utilities.Address;
|
import org.session.libsession.utilities.Address;
|
||||||
import org.session.libsession.utilities.Contact;
|
import org.session.libsession.utilities.Contact;
|
||||||
import org.session.libsession.utilities.ServiceUtil;
|
import org.session.libsession.utilities.ServiceUtil;
|
||||||
@ -137,7 +138,7 @@ public class DefaultMessageNotifier implements MessageNotifier {
|
|||||||
Intent intent = new Intent(context, ConversationActivityV2.class);
|
Intent intent = new Intent(context, ConversationActivityV2.class);
|
||||||
intent.putExtra(ConversationActivityV2.ADDRESS, recipient.getAddress());
|
intent.putExtra(ConversationActivityV2.ADDRESS, recipient.getAddress());
|
||||||
intent.putExtra(ConversationActivityV2.THREAD_ID, threadId);
|
intent.putExtra(ConversationActivityV2.THREAD_ID, threadId);
|
||||||
intent.setData((Uri.parse("custom://" + System.currentTimeMillis())));
|
intent.setData((Uri.parse("custom://" + SnodeAPI.getNowWithOffset())));
|
||||||
|
|
||||||
FailedNotificationBuilder builder = new FailedNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context), intent);
|
FailedNotificationBuilder builder = new FailedNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context), intent);
|
||||||
((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE))
|
((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE))
|
||||||
|
@ -82,7 +82,7 @@ public class MarkReadReceiver extends BroadcastReceiver {
|
|||||||
List<Long> timestamps = Stream.of(addressMap.get(address)).map(SyncMessageId::getTimetamp).toList();
|
List<Long> timestamps = Stream.of(addressMap.get(address)).map(SyncMessageId::getTimetamp).toList();
|
||||||
if (!SessionMetaProtocol.shouldSendReadReceipt(Recipient.from(context, address, false))) { continue; }
|
if (!SessionMetaProtocol.shouldSendReadReceipt(Recipient.from(context, address, false))) { continue; }
|
||||||
ReadReceipt readReceipt = new ReadReceipt(timestamps);
|
ReadReceipt readReceipt = new ReadReceipt(timestamps);
|
||||||
readReceipt.setSentTimestamp(System.currentTimeMillis());
|
readReceipt.setSentTimestamp(SnodeAPI.getNowWithOffset());
|
||||||
MessageSender.send(readReceipt, address);
|
MessageSender.send(readReceipt, address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ class DefaultConversationRepository @Inject constructor(
|
|||||||
val openGroup = lokiThreadDb.getOpenGroupChat(threadId) ?: return
|
val openGroup = lokiThreadDb.getOpenGroupChat(threadId) ?: return
|
||||||
for (contact in contacts) {
|
for (contact in contacts) {
|
||||||
val message = VisibleMessage()
|
val message = VisibleMessage()
|
||||||
message.sentTimestamp = System.currentTimeMillis()
|
message.sentTimestamp = SnodeAPI.nowWithOffset
|
||||||
val openGroupInvitation = OpenGroupInvitation()
|
val openGroupInvitation = OpenGroupInvitation()
|
||||||
openGroupInvitation.name = openGroup.name
|
openGroupInvitation.name = openGroup.name
|
||||||
openGroupInvitation.url = openGroup.joinURL
|
openGroupInvitation.url = openGroup.joinURL
|
||||||
|
@ -9,6 +9,7 @@ import android.widget.Toast;
|
|||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
||||||
|
import org.session.libsession.snode.SnodeAPI;
|
||||||
import org.session.libsession.utilities.Address;
|
import org.session.libsession.utilities.Address;
|
||||||
import org.session.libsignal.utilities.Log;
|
import org.session.libsignal.utilities.Log;
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||||
@ -50,7 +51,7 @@ public class QuickResponseService extends IntentService {
|
|||||||
if (!TextUtils.isEmpty(content)) {
|
if (!TextUtils.isEmpty(content)) {
|
||||||
VisibleMessage message = new VisibleMessage();
|
VisibleMessage message = new VisibleMessage();
|
||||||
message.setText(content);
|
message.setText(content);
|
||||||
message.setSentTimestamp(System.currentTimeMillis());
|
message.setSentTimestamp(SnodeAPI.getNowWithOffset());
|
||||||
MessageSender.send(message, Address.fromExternal(this, number));
|
MessageSender.send(message, Address.fromExternal(this, number));
|
||||||
}
|
}
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package org.thoughtcrime.securesms.service
|
package org.thoughtcrime.securesms.service
|
||||||
|
|
||||||
import android.app.Service
|
import android.app.ForegroundServiceStartNotAllowedException
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -17,6 +17,8 @@ import android.telephony.PhoneStateListener.LISTEN_NONE
|
|||||||
import android.telephony.TelephonyManager
|
import android.telephony.TelephonyManager
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
|
import androidx.lifecycle.LifecycleService
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import org.session.libsession.messaging.calls.CallMessageType
|
import org.session.libsession.messaging.calls.CallMessageType
|
||||||
@ -25,6 +27,7 @@ import org.session.libsession.utilities.FutureTaskListener
|
|||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import org.thoughtcrime.securesms.calls.WebRtcCallActivity
|
import org.thoughtcrime.securesms.calls.WebRtcCallActivity
|
||||||
|
import org.thoughtcrime.securesms.notifications.BackgroundPollWorker
|
||||||
import org.thoughtcrime.securesms.util.CallNotificationBuilder
|
import org.thoughtcrime.securesms.util.CallNotificationBuilder
|
||||||
import org.thoughtcrime.securesms.util.CallNotificationBuilder.Companion.TYPE_ESTABLISHED
|
import org.thoughtcrime.securesms.util.CallNotificationBuilder.Companion.TYPE_ESTABLISHED
|
||||||
import org.thoughtcrime.securesms.util.CallNotificationBuilder.Companion.TYPE_INCOMING_CONNECTING
|
import org.thoughtcrime.securesms.util.CallNotificationBuilder.Companion.TYPE_INCOMING_CONNECTING
|
||||||
@ -46,7 +49,7 @@ import javax.inject.Inject
|
|||||||
import org.thoughtcrime.securesms.webrtc.data.State as CallState
|
import org.thoughtcrime.securesms.webrtc.data.State as CallState
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class WebRtcCallService : Service(), CallManager.WebRtcListener {
|
class WebRtcCallService : LifecycleService(), CallManager.WebRtcListener {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@ -238,8 +241,11 @@ class WebRtcCallService : Service(), CallManager.WebRtcListener {
|
|||||||
scheduledReconnect?.cancel(false)
|
scheduledReconnect?.cancel(false)
|
||||||
scheduledTimeout = null
|
scheduledTimeout = null
|
||||||
scheduledReconnect = null
|
scheduledReconnect = null
|
||||||
|
|
||||||
|
lifecycleScope.launchWhenCreated {
|
||||||
stopForeground(true)
|
stopForeground(true)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun isSameCall(intent: Intent): Boolean {
|
private fun isSameCall(intent: Intent): Boolean {
|
||||||
val expectedCallId = getCallId(intent)
|
val expectedCallId = getCallId(intent)
|
||||||
@ -253,7 +259,9 @@ class WebRtcCallService : Service(), CallManager.WebRtcListener {
|
|||||||
|
|
||||||
private fun isIdle() = callManager.isIdle()
|
private fun isIdle() = callManager.isIdle()
|
||||||
|
|
||||||
override fun onBind(intent: Intent?): IBinder? = null
|
override fun onBind(intent: Intent): IBinder? {
|
||||||
|
return super.onBind(intent)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onHangup() {
|
override fun onHangup() {
|
||||||
serviceExecutor.execute {
|
serviceExecutor.execute {
|
||||||
@ -272,7 +280,8 @@ class WebRtcCallService : Service(), CallManager.WebRtcListener {
|
|||||||
if (intent == null || intent.action == null) return START_NOT_STICKY
|
if (intent == null || intent.action == null) return START_NOT_STICKY
|
||||||
serviceExecutor.execute {
|
serviceExecutor.execute {
|
||||||
val action = intent.action
|
val action = intent.action
|
||||||
Log.i("Loki", "Handling ${intent.action}")
|
val callId = ((intent.getSerializableExtra(EXTRA_CALL_ID) as? UUID)?.toString() ?: "No callId")
|
||||||
|
Log.i("Loki", "Handling ${intent.action} for call: ${callId}")
|
||||||
when {
|
when {
|
||||||
action == ACTION_INCOMING_RING && isSameCall(intent) && callManager.currentConnectionState == CallState.Reconnecting -> handleNewOffer(
|
action == ACTION_INCOMING_RING && isSameCall(intent) && callManager.currentConnectionState == CallState.Reconnecting -> handleNewOffer(
|
||||||
intent
|
intent
|
||||||
@ -361,9 +370,11 @@ class WebRtcCallService : Service(), CallManager.WebRtcListener {
|
|||||||
insertMissedCall(recipient, false)
|
insertMissedCall(recipient, false)
|
||||||
|
|
||||||
if (callState == CallState.Idle) {
|
if (callState == CallState.Idle) {
|
||||||
|
lifecycleScope.launchWhenCreated {
|
||||||
stopForeground(true)
|
stopForeground(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleUpdateAudio(intent: Intent) {
|
private fun handleUpdateAudio(intent: Intent) {
|
||||||
val audioCommand = intent.getParcelableExtra<AudioManagerCommand>(EXTRA_AUDIO_COMMAND)!!
|
val audioCommand = intent.getParcelableExtra<AudioManagerCommand>(EXTRA_AUDIO_COMMAND)!!
|
||||||
@ -409,6 +420,11 @@ class WebRtcCallService : Service(), CallManager.WebRtcListener {
|
|||||||
callManager.initializeAudioForCall()
|
callManager.initializeAudioForCall()
|
||||||
callManager.startIncomingRinger()
|
callManager.startIncomingRinger()
|
||||||
callManager.setAudioEnabled(true)
|
callManager.setAudioEnabled(true)
|
||||||
|
|
||||||
|
BackgroundPollWorker.scheduleOnce(
|
||||||
|
this,
|
||||||
|
arrayOf(BackgroundPollWorker.Targets.DMS)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,7 +589,9 @@ class WebRtcCallService : Service(), CallManager.WebRtcListener {
|
|||||||
private fun handleRemoteHangup(intent: Intent) {
|
private fun handleRemoteHangup(intent: Intent) {
|
||||||
if (callManager.callId != getCallId(intent)) {
|
if (callManager.callId != getCallId(intent)) {
|
||||||
Log.e(TAG, "Hangup for non-active call...")
|
Log.e(TAG, "Hangup for non-active call...")
|
||||||
|
lifecycleScope.launchWhenCreated {
|
||||||
stopForeground(true)
|
stopForeground(true)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -717,10 +735,16 @@ class WebRtcCallService : Service(), CallManager.WebRtcListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setCallInProgressNotification(type: Int, recipient: Recipient?) {
|
private fun setCallInProgressNotification(type: Int, recipient: Recipient?) {
|
||||||
|
try {
|
||||||
startForeground(
|
startForeground(
|
||||||
CallNotificationBuilder.WEBRTC_NOTIFICATION,
|
CallNotificationBuilder.WEBRTC_NOTIFICATION,
|
||||||
CallNotificationBuilder.getCallInProgressNotification(this, type, recipient)
|
CallNotificationBuilder.getCallInProgressNotification(this, type, recipient)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
catch(e: ForegroundServiceStartNotAllowedException) {
|
||||||
|
Log.e(TAG, "Failed to setCallInProgressNotification as a foreground service for type: ${type}, trying to update instead")
|
||||||
|
}
|
||||||
|
|
||||||
if (!CallNotificationBuilder.areNotificationsEnabled(this) && type == TYPE_INCOMING_PRE_OFFER) {
|
if (!CallNotificationBuilder.areNotificationsEnabled(this) && type == TYPE_INCOMING_PRE_OFFER) {
|
||||||
// start an intent for the fullscreen
|
// start an intent for the fullscreen
|
||||||
val foregroundIntent = Intent(this, WebRtcCallActivity::class.java)
|
val foregroundIntent = Intent(this, WebRtcCallActivity::class.java)
|
||||||
@ -769,10 +793,14 @@ class WebRtcCallService : Service(), CallManager.WebRtcListener {
|
|||||||
callReceiver?.let { receiver ->
|
callReceiver?.let { receiver ->
|
||||||
unregisterReceiver(receiver)
|
unregisterReceiver(receiver)
|
||||||
}
|
}
|
||||||
|
wiredHeadsetStateReceiver?.let { unregisterReceiver(it) }
|
||||||
|
powerButtonReceiver?.let { unregisterReceiver(it) }
|
||||||
networkChangedReceiver?.unregister(this)
|
networkChangedReceiver?.unregister(this)
|
||||||
wantsToAnswerReceiver?.let { receiver ->
|
wantsToAnswerReceiver?.let { receiver ->
|
||||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver)
|
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver)
|
||||||
}
|
}
|
||||||
|
powerButtonReceiver = null
|
||||||
|
wiredHeadsetStateReceiver = null
|
||||||
networkChangedReceiver = null
|
networkChangedReceiver = null
|
||||||
callReceiver = null
|
callReceiver = null
|
||||||
uncaughtExceptionHandlerManager?.unregister()
|
uncaughtExceptionHandlerManager?.unregister()
|
||||||
|
@ -18,6 +18,7 @@ import org.session.libsession.database.StorageProtocol
|
|||||||
import org.session.libsession.messaging.calls.CallMessageType
|
import org.session.libsession.messaging.calls.CallMessageType
|
||||||
import org.session.libsession.messaging.messages.control.CallMessage
|
import org.session.libsession.messaging.messages.control.CallMessage
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||||
|
import org.session.libsession.snode.SnodeAPI
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.session.libsession.utilities.Debouncer
|
import org.session.libsession.utilities.Debouncer
|
||||||
import org.session.libsession.utilities.Util
|
import org.session.libsession.utilities.Util
|
||||||
@ -574,7 +575,7 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun insertCallMessage(threadPublicKey: String, callMessageType: CallMessageType, signal: Boolean = false, sentTimestamp: Long = System.currentTimeMillis()) {
|
fun insertCallMessage(threadPublicKey: String, callMessageType: CallMessageType, signal: Boolean = false, sentTimestamp: Long = SnodeAPI.nowWithOffset) {
|
||||||
storage.insertCallMessage(threadPublicKey, callMessageType, sentTimestamp)
|
storage.insertCallMessage(threadPublicKey, callMessageType, sentTimestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.webrtc
|
|||||||
|
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.coroutineScope
|
import androidx.lifecycle.coroutineScope
|
||||||
@ -32,6 +33,20 @@ class CallMessageProcessor(private val context: Context, private val textSecureP
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val VERY_EXPIRED_TIME = 15 * 60 * 1000L
|
private const val VERY_EXPIRED_TIME = 15 * 60 * 1000L
|
||||||
|
|
||||||
|
fun safeStartService(context: Context, intent: Intent) {
|
||||||
|
// If the foreground service crashes then it's possible for one of these intents to
|
||||||
|
// be started in the background (in which case 'startService' will throw a
|
||||||
|
// 'BackgroundServiceStartNotAllowedException' exception) so catch that case and try
|
||||||
|
// to re-start the service in the foreground
|
||||||
|
try { context.startService(intent) }
|
||||||
|
catch(e: Exception) {
|
||||||
|
try { ContextCompat.startForegroundService(context, intent) }
|
||||||
|
catch (e2: Exception) {
|
||||||
|
Log.e("Loki", "Unable to start CallMessage intent: ${e2.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -90,7 +105,7 @@ class CallMessageProcessor(private val context: Context, private val textSecureP
|
|||||||
private fun incomingHangup(callMessage: CallMessage) {
|
private fun incomingHangup(callMessage: CallMessage) {
|
||||||
val callId = callMessage.callId ?: return
|
val callId = callMessage.callId ?: return
|
||||||
val hangupIntent = WebRtcCallService.remoteHangupIntent(context, callId)
|
val hangupIntent = WebRtcCallService.remoteHangupIntent(context, callId)
|
||||||
context.startService(hangupIntent)
|
safeStartService(context, hangupIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun incomingAnswer(callMessage: CallMessage) {
|
private fun incomingAnswer(callMessage: CallMessage) {
|
||||||
@ -103,7 +118,8 @@ class CallMessageProcessor(private val context: Context, private val textSecureP
|
|||||||
sdp = sdp,
|
sdp = sdp,
|
||||||
callId = callId
|
callId = callId
|
||||||
)
|
)
|
||||||
context.startService(answerIntent)
|
|
||||||
|
safeStartService(context, answerIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleIceCandidates(callMessage: CallMessage) {
|
private fun handleIceCandidates(callMessage: CallMessage) {
|
||||||
@ -119,7 +135,7 @@ class CallMessageProcessor(private val context: Context, private val textSecureP
|
|||||||
callId = callId,
|
callId = callId,
|
||||||
address = Address.fromSerialized(sender)
|
address = Address.fromSerialized(sender)
|
||||||
)
|
)
|
||||||
context.startService(iceIntent)
|
safeStartService(context, iceIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun incomingPreOffer(callMessage: CallMessage) {
|
private fun incomingPreOffer(callMessage: CallMessage) {
|
||||||
@ -132,7 +148,7 @@ class CallMessageProcessor(private val context: Context, private val textSecureP
|
|||||||
callId = callId,
|
callId = callId,
|
||||||
callTime = callMessage.sentTimestamp!!
|
callTime = callMessage.sentTimestamp!!
|
||||||
)
|
)
|
||||||
context.startService(incomingIntent)
|
safeStartService(context, incomingIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun incomingCall(callMessage: CallMessage) {
|
private fun incomingCall(callMessage: CallMessage) {
|
||||||
@ -146,7 +162,7 @@ class CallMessageProcessor(private val context: Context, private val textSecureP
|
|||||||
callId = callId,
|
callId = callId,
|
||||||
callTime = callMessage.sentTimestamp!!
|
callTime = callMessage.sentTimestamp!!
|
||||||
)
|
)
|
||||||
context.startService(incomingIntent)
|
safeStartService(context, incomingIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CallMessage.iceCandidates(): List<IceCandidate> {
|
private fun CallMessage.iceCandidates(): List<IceCandidate> {
|
||||||
|
@ -98,19 +98,19 @@ class BatchMessageReceiveJob(
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
when (e) {
|
when (e) {
|
||||||
is MessageReceiver.Error.DuplicateMessage, MessageReceiver.Error.SelfSend -> {
|
is MessageReceiver.Error.DuplicateMessage, MessageReceiver.Error.SelfSend -> {
|
||||||
Log.i(TAG, "Couldn't receive message, failed with error: ${e.message}")
|
Log.i(TAG, "Couldn't receive message, failed with error: ${e.message} (id: $id)")
|
||||||
}
|
}
|
||||||
is MessageReceiver.Error -> {
|
is MessageReceiver.Error -> {
|
||||||
if (!e.isRetryable) {
|
if (!e.isRetryable) {
|
||||||
Log.e(TAG, "Couldn't receive message, failed permanently", e)
|
Log.e(TAG, "Couldn't receive message, failed permanently (id: $id)", e)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Log.e(TAG, "Couldn't receive message, failed", e)
|
Log.e(TAG, "Couldn't receive message, failed (id: $id)", e)
|
||||||
failures += messageParameters
|
failures += messageParameters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
Log.e(TAG, "Couldn't receive message, failed", e)
|
Log.e(TAG, "Couldn't receive message, failed (id: $id)", e)
|
||||||
failures += messageParameters
|
failures += messageParameters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,11 +166,11 @@ class BatchMessageReceiveJob(
|
|||||||
else -> MessageReceiver.handle(message, proto, openGroupID)
|
else -> MessageReceiver.handle(message, proto, openGroupID)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Couldn't process message.", e)
|
Log.e(TAG, "Couldn't process message (id: $id)", e)
|
||||||
if (e is MessageReceiver.Error && !e.isRetryable) {
|
if (e is MessageReceiver.Error && !e.isRetryable) {
|
||||||
Log.e(TAG, "Message failed permanently",e)
|
Log.e(TAG, "Message failed permanently (id: $id)", e)
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "Message failed",e)
|
Log.e(TAG, "Message failed (id: $id)", e)
|
||||||
failures += parameters
|
failures += parameters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,12 +199,12 @@ class BatchMessageReceiveJob(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSuccess(dispatcherName: String) {
|
private fun handleSuccess(dispatcherName: String) {
|
||||||
Log.i(TAG, "Completed processing of ${messages.size} messages")
|
Log.i(TAG, "Completed processing of ${messages.size} messages (id: $id)")
|
||||||
this.delegate?.handleJobSucceeded(this, dispatcherName)
|
this.delegate?.handleJobSucceeded(this, dispatcherName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleFailure(dispatcherName: String) {
|
private fun handleFailure(dispatcherName: String) {
|
||||||
Log.i(TAG, "Handling failure of ${failures.size} messages (${messages.size - failures.size} processed successfully)")
|
Log.i(TAG, "Handling failure of ${failures.size} messages (${messages.size - failures.size} processed successfully) (id: $id)")
|
||||||
this.delegate?.handleJobFailed(this, dispatcherName, Exception("One or more jobs resulted in failure"))
|
this.delegate?.handleJobFailed(this, dispatcherName, Exception("One or more jobs resulted in failure"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package org.session.libsession.messaging.jobs
|
|||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.session.libsession.messaging.utilities.Data
|
import org.session.libsession.messaging.utilities.Data
|
||||||
|
import org.session.libsession.snode.SnodeAPI
|
||||||
import org.session.libsession.utilities.GroupUtil
|
import org.session.libsession.utilities.GroupUtil
|
||||||
|
|
||||||
class GroupAvatarDownloadJob(val server: String, val room: String, val imageId: String?) : Job {
|
class GroupAvatarDownloadJob(val server: String, val room: String, val imageId: String?) : Job {
|
||||||
@ -43,7 +44,7 @@ class GroupAvatarDownloadJob(val server: String, val room: String, val imageId:
|
|||||||
|
|
||||||
val groupId = GroupUtil.getEncodedOpenGroupID("$server.$room".toByteArray())
|
val groupId = GroupUtil.getEncodedOpenGroupID("$server.$room".toByteArray())
|
||||||
storage.updateProfilePicture(groupId, bytes)
|
storage.updateProfilePicture(groupId, bytes)
|
||||||
storage.updateTimestampUpdated(groupId, System.currentTimeMillis())
|
storage.updateTimestampUpdated(groupId, SnodeAPI.nowWithOffset)
|
||||||
delegate?.handleJobSucceeded(this, dispatcherName)
|
delegate?.handleJobSucceeded(this, dispatcherName)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
delegate?.handleJobFailed(this, dispatcherName, e)
|
delegate?.handleJobFailed(this, dispatcherName, e)
|
||||||
|
@ -23,6 +23,7 @@ import org.session.libsession.messaging.utilities.SessionId
|
|||||||
import org.session.libsession.messaging.utilities.SodiumUtilities
|
import org.session.libsession.messaging.utilities.SodiumUtilities
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
import org.session.libsession.snode.OnionResponse
|
import org.session.libsession.snode.OnionResponse
|
||||||
|
import org.session.libsession.snode.SnodeAPI
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.utilities.Base64.decode
|
import org.session.libsignal.utilities.Base64.decode
|
||||||
import org.session.libsignal.utilities.Base64.encodeBytes
|
import org.session.libsignal.utilities.Base64.encodeBytes
|
||||||
@ -303,7 +304,7 @@ object OpenGroupApi {
|
|||||||
val headers = request.headers.toMutableMap()
|
val headers = request.headers.toMutableMap()
|
||||||
if (request.isAuthRequired) {
|
if (request.isAuthRequired) {
|
||||||
val nonce = sodium.nonce(16)
|
val nonce = sodium.nonce(16)
|
||||||
val timestamp = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())
|
val timestamp = TimeUnit.MILLISECONDS.toSeconds(SnodeAPI.nowWithOffset)
|
||||||
var pubKey = ""
|
var pubKey = ""
|
||||||
var signature = ByteArray(Sign.BYTES)
|
var signature = ByteArray(Sign.BYTES)
|
||||||
var bodyHash = ByteArray(0)
|
var bodyHash = ByteArray(0)
|
||||||
|
@ -15,6 +15,7 @@ import org.session.libsession.messaging.messages.control.UnsendRequest
|
|||||||
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
||||||
import org.session.libsession.messaging.utilities.SessionId
|
import org.session.libsession.messaging.utilities.SessionId
|
||||||
import org.session.libsession.messaging.utilities.SodiumUtilities
|
import org.session.libsession.messaging.utilities.SodiumUtilities
|
||||||
|
import org.session.libsession.snode.SnodeAPI
|
||||||
import org.session.libsignal.crypto.PushTransportDetails
|
import org.session.libsignal.crypto.PushTransportDetails
|
||||||
import org.session.libsignal.protos.SignalServiceProtos
|
import org.session.libsignal.protos.SignalServiceProtos
|
||||||
import org.session.libsignal.utilities.IdPrefix
|
import org.session.libsignal.utilities.IdPrefix
|
||||||
@ -156,7 +157,7 @@ object MessageReceiver {
|
|||||||
message.sender = sender
|
message.sender = sender
|
||||||
message.recipient = userPublicKey
|
message.recipient = userPublicKey
|
||||||
message.sentTimestamp = envelope.timestamp
|
message.sentTimestamp = envelope.timestamp
|
||||||
message.receivedTimestamp = if (envelope.hasServerTimestamp()) envelope.serverTimestamp else System.currentTimeMillis()
|
message.receivedTimestamp = if (envelope.hasServerTimestamp()) envelope.serverTimestamp else SnodeAPI.nowWithOffset
|
||||||
message.groupPublicKey = groupPublicKey
|
message.groupPublicKey = groupPublicKey
|
||||||
message.openGroupServerMessageID = openGroupServerID
|
message.openGroupServerMessageID = openGroupServerID
|
||||||
// Validate
|
// Validate
|
||||||
|
@ -199,7 +199,20 @@ object MessageSender {
|
|||||||
val hash = it["hash"] as? String
|
val hash = it["hash"] as? String
|
||||||
message.serverHash = hash
|
message.serverHash = hash
|
||||||
handleSuccessfulMessageSend(message, destination, isSyncMessage)
|
handleSuccessfulMessageSend(message, destination, isSyncMessage)
|
||||||
val shouldNotify = ((message is VisibleMessage || message is UnsendRequest || message is CallMessage) && !isSyncMessage)
|
|
||||||
|
val shouldNotify: Boolean = when (message) {
|
||||||
|
is VisibleMessage, is UnsendRequest -> !isSyncMessage
|
||||||
|
is CallMessage -> {
|
||||||
|
// Note: Other 'CallMessage' types are too big to send as push notifications
|
||||||
|
// so only send the 'preOffer' message as a notification
|
||||||
|
when (message.type) {
|
||||||
|
SignalServiceProtos.CallMessage.Type.PRE_OFFER -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (message is ClosedGroupControlMessage && message.kind is ClosedGroupControlMessage.Kind.New) {
|
if (message is ClosedGroupControlMessage && message.kind is ClosedGroupControlMessage.Kind.New) {
|
||||||
shouldNotify = true
|
shouldNotify = true
|
||||||
@ -229,7 +242,7 @@ object MessageSender {
|
|||||||
val deferred = deferred<Unit, Exception>()
|
val deferred = deferred<Unit, Exception>()
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
if (message.sentTimestamp == null) {
|
if (message.sentTimestamp == null) {
|
||||||
message.sentTimestamp = System.currentTimeMillis()
|
message.sentTimestamp = SnodeAPI.nowWithOffset
|
||||||
}
|
}
|
||||||
val userEdKeyPair = MessagingModuleConfiguration.shared.getUserED25519KeyPair()!!
|
val userEdKeyPair = MessagingModuleConfiguration.shared.getUserED25519KeyPair()!!
|
||||||
var serverCapabilities = listOf<String>()
|
var serverCapabilities = listOf<String>()
|
||||||
|
@ -50,11 +50,11 @@ fun MessageSender.create(name: String, members: Collection<String>): Promise<Str
|
|||||||
val admins = setOf( userPublicKey )
|
val admins = setOf( userPublicKey )
|
||||||
val adminsAsData = admins.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) }
|
val adminsAsData = admins.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) }
|
||||||
storage.createGroup(groupID, name, LinkedList(members.map { Address.fromSerialized(it) }),
|
storage.createGroup(groupID, name, LinkedList(members.map { Address.fromSerialized(it) }),
|
||||||
null, null, LinkedList(admins.map { Address.fromSerialized(it) }), System.currentTimeMillis())
|
null, null, LinkedList(admins.map { Address.fromSerialized(it) }), SnodeAPI.nowWithOffset)
|
||||||
storage.setProfileSharing(Address.fromSerialized(groupID), true)
|
storage.setProfileSharing(Address.fromSerialized(groupID), true)
|
||||||
// Send a closed group update message to all members individually
|
// Send a closed group update message to all members individually
|
||||||
val closedGroupUpdateKind = ClosedGroupControlMessage.Kind.New(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), name, encryptionKeyPair, membersAsData, adminsAsData, 0)
|
val closedGroupUpdateKind = ClosedGroupControlMessage.Kind.New(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), name, encryptionKeyPair, membersAsData, adminsAsData, 0)
|
||||||
val sentTime = System.currentTimeMillis()
|
val sentTime = SnodeAPI.nowWithOffset
|
||||||
for (member in members) {
|
for (member in members) {
|
||||||
val closedGroupControlMessage = ClosedGroupControlMessage(closedGroupUpdateKind)
|
val closedGroupControlMessage = ClosedGroupControlMessage(closedGroupUpdateKind)
|
||||||
closedGroupControlMessage.sentTimestamp = sentTime
|
closedGroupControlMessage.sentTimestamp = sentTime
|
||||||
@ -97,7 +97,7 @@ fun MessageSender.setName(groupPublicKey: String, newName: String) {
|
|||||||
val admins = group.admins.map { it.serialize() }
|
val admins = group.admins.map { it.serialize() }
|
||||||
// Send the update to the group
|
// Send the update to the group
|
||||||
val kind = ClosedGroupControlMessage.Kind.NameChange(newName)
|
val kind = ClosedGroupControlMessage.Kind.NameChange(newName)
|
||||||
val sentTime = System.currentTimeMillis()
|
val sentTime = SnodeAPI.nowWithOffset
|
||||||
val closedGroupControlMessage = ClosedGroupControlMessage(kind)
|
val closedGroupControlMessage = ClosedGroupControlMessage(kind)
|
||||||
closedGroupControlMessage.sentTimestamp = sentTime
|
closedGroupControlMessage.sentTimestamp = sentTime
|
||||||
send(closedGroupControlMessage, Address.fromSerialized(groupID))
|
send(closedGroupControlMessage, Address.fromSerialized(groupID))
|
||||||
@ -137,7 +137,7 @@ fun MessageSender.addMembers(groupPublicKey: String, membersToAdd: List<String>)
|
|||||||
val name = group.title
|
val name = group.title
|
||||||
// Send the update to the group
|
// Send the update to the group
|
||||||
val memberUpdateKind = ClosedGroupControlMessage.Kind.MembersAdded(newMembersAsData)
|
val memberUpdateKind = ClosedGroupControlMessage.Kind.MembersAdded(newMembersAsData)
|
||||||
val sentTime = System.currentTimeMillis()
|
val sentTime = SnodeAPI.nowWithOffset
|
||||||
val closedGroupControlMessage = ClosedGroupControlMessage(memberUpdateKind)
|
val closedGroupControlMessage = ClosedGroupControlMessage(memberUpdateKind)
|
||||||
closedGroupControlMessage.sentTimestamp = sentTime
|
closedGroupControlMessage.sentTimestamp = sentTime
|
||||||
send(closedGroupControlMessage, Address.fromSerialized(groupID))
|
send(closedGroupControlMessage, Address.fromSerialized(groupID))
|
||||||
@ -151,7 +151,7 @@ fun MessageSender.addMembers(groupPublicKey: String, membersToAdd: List<String>)
|
|||||||
// updates from before that timestamp. By setting the timestamp of the message below to a value
|
// updates from before that timestamp. By setting the timestamp of the message below to a value
|
||||||
// greater than that of the `MembersAdded` message, we ensure that newly added members ignore
|
// greater than that of the `MembersAdded` message, we ensure that newly added members ignore
|
||||||
// the `MembersAdded` message.
|
// the `MembersAdded` message.
|
||||||
closedGroupControlMessage.sentTimestamp = System.currentTimeMillis()
|
closedGroupControlMessage.sentTimestamp = SnodeAPI.nowWithOffset
|
||||||
send(closedGroupControlMessage, Address.fromSerialized(member))
|
send(closedGroupControlMessage, Address.fromSerialized(member))
|
||||||
}
|
}
|
||||||
// Notify the user
|
// Notify the user
|
||||||
@ -192,7 +192,7 @@ fun MessageSender.removeMembers(groupPublicKey: String, membersToRemove: List<St
|
|||||||
val name = group.title
|
val name = group.title
|
||||||
// Send the update to the group
|
// Send the update to the group
|
||||||
val memberUpdateKind = ClosedGroupControlMessage.Kind.MembersRemoved(removeMembersAsData)
|
val memberUpdateKind = ClosedGroupControlMessage.Kind.MembersRemoved(removeMembersAsData)
|
||||||
val sentTime = System.currentTimeMillis()
|
val sentTime = SnodeAPI.nowWithOffset
|
||||||
val closedGroupControlMessage = ClosedGroupControlMessage(memberUpdateKind)
|
val closedGroupControlMessage = ClosedGroupControlMessage(memberUpdateKind)
|
||||||
closedGroupControlMessage.sentTimestamp = sentTime
|
closedGroupControlMessage.sentTimestamp = sentTime
|
||||||
send(closedGroupControlMessage, Address.fromSerialized(groupID))
|
send(closedGroupControlMessage, Address.fromSerialized(groupID))
|
||||||
@ -223,7 +223,7 @@ fun MessageSender.leave(groupPublicKey: String, notifyUser: Boolean = true): Pro
|
|||||||
val name = group.title
|
val name = group.title
|
||||||
// Send the update to the group
|
// Send the update to the group
|
||||||
val closedGroupControlMessage = ClosedGroupControlMessage(ClosedGroupControlMessage.Kind.MemberLeft())
|
val closedGroupControlMessage = ClosedGroupControlMessage(ClosedGroupControlMessage.Kind.MemberLeft())
|
||||||
val sentTime = System.currentTimeMillis()
|
val sentTime = SnodeAPI.nowWithOffset
|
||||||
closedGroupControlMessage.sentTimestamp = sentTime
|
closedGroupControlMessage.sentTimestamp = sentTime
|
||||||
storage.setActive(groupID, false)
|
storage.setActive(groupID, false)
|
||||||
sendNonDurably(closedGroupControlMessage, Address.fromSerialized(groupID)).success {
|
sendNonDurably(closedGroupControlMessage, Address.fromSerialized(groupID)).success {
|
||||||
|
@ -54,10 +54,9 @@ class ClosedGroupPollerV2 {
|
|||||||
setUpPolling(groupPublicKey)
|
setUpPolling(groupPublicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stop() {
|
fun stopAll() {
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
futures.forEach { it.value.cancel(false) }
|
||||||
val allGroupPublicKeys = storage.getAllClosedGroupPublicKeys()
|
isPolling.forEach { isPolling[it.key] = false }
|
||||||
allGroupPublicKeys.iterator().forEach { stopPolling(it) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopPolling(groupPublicKey: String) {
|
fun stopPolling(groupPublicKey: String) {
|
||||||
|
@ -26,6 +26,7 @@ import org.session.libsignal.utilities.ThreadUtils
|
|||||||
import org.session.libsignal.utilities.recover
|
import org.session.libsignal.utilities.recover
|
||||||
import org.session.libsignal.utilities.toHexString
|
import org.session.libsignal.utilities.toHexString
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
|
|
||||||
private typealias Path = List<Snode>
|
private typealias Path = List<Snode>
|
||||||
@ -43,13 +44,27 @@ object OnionRequestAPI {
|
|||||||
private val snodeFailureCount = mutableMapOf<Snode, Int>()
|
private val snodeFailureCount = mutableMapOf<Snode, Int>()
|
||||||
|
|
||||||
var guardSnodes = setOf<Snode>()
|
var guardSnodes = setOf<Snode>()
|
||||||
|
var _paths: AtomicReference<List<Path>?> = AtomicReference(null)
|
||||||
var paths: List<Path> // Not a set to ensure we consistently show the same path to the user
|
var paths: List<Path> // Not a set to ensure we consistently show the same path to the user
|
||||||
get() = database.getOnionRequestPaths()
|
get() {
|
||||||
|
val paths = _paths.get()
|
||||||
|
|
||||||
|
if (paths != null) { return paths }
|
||||||
|
|
||||||
|
// Storing this in an atomic variable as it was causing a number of background
|
||||||
|
// ANRs when this value was accessed via the main thread after tapping on
|
||||||
|
// a notification)
|
||||||
|
val result = database.getOnionRequestPaths()
|
||||||
|
_paths.set(result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
set(newValue) {
|
set(newValue) {
|
||||||
if (newValue.isEmpty()) {
|
if (newValue.isEmpty()) {
|
||||||
database.clearOnionRequestPaths()
|
database.clearOnionRequestPaths()
|
||||||
|
_paths.set(null)
|
||||||
} else {
|
} else {
|
||||||
database.setOnionRequestPaths(newValue)
|
database.setOnionRequestPaths(newValue)
|
||||||
|
_paths.set(newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ object SnodeAPI {
|
|||||||
internal var clockOffset = 0L
|
internal var clockOffset = 0L
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val nowWithOffset
|
public val nowWithOffset
|
||||||
get() = System.currentTimeMillis() + clockOffset
|
get() = System.currentTimeMillis() + clockOffset
|
||||||
|
|
||||||
internal var forkInfo by observable(database.getForkInfo()) { _, oldValue, newValue ->
|
internal var forkInfo by observable(database.getForkInfo()) { _, oldValue, newValue ->
|
||||||
@ -590,7 +590,7 @@ object SnodeAPI {
|
|||||||
val parameters = message.toJSON().toMutableMap<String,Any>()
|
val parameters = message.toJSON().toMutableMap<String,Any>()
|
||||||
// Construct signature
|
// Construct signature
|
||||||
if (requiresAuth) {
|
if (requiresAuth) {
|
||||||
val sigTimestamp = System.currentTimeMillis() + SnodeAPI.clockOffset
|
val sigTimestamp = nowWithOffset
|
||||||
val ed25519PublicKey = userED25519KeyPair.publicKey.asHexString
|
val ed25519PublicKey = userED25519KeyPair.publicKey.asHexString
|
||||||
val signature = ByteArray(Sign.BYTES)
|
val signature = ByteArray(Sign.BYTES)
|
||||||
// assume namespace here is non-zero, as zero namespace doesn't require auth
|
// assume namespace here is non-zero, as zero namespace doesn't require auth
|
||||||
|
@ -26,7 +26,7 @@ class SSKEnvironment(
|
|||||||
|
|
||||||
interface ProfileManagerProtocol {
|
interface ProfileManagerProtocol {
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME_PADDED_LENGTH = 26
|
const val NAME_PADDED_LENGTH = 64
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setNickname(context: Context, recipient: Recipient, nickname: String?)
|
fun setNickname(context: Context, recipient: Recipient, nickname: String?)
|
||||||
|
@ -319,8 +319,8 @@ public class SignalServiceDataMessage {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SignalServiceDataMessage build() {
|
public SignalServiceDataMessage build(long fallbackTimestamp) {
|
||||||
if (timestamp == 0) timestamp = System.currentTimeMillis();
|
if (timestamp == 0) timestamp = fallbackTimestamp;
|
||||||
// closedGroupUpdate is always null because we don't use SignalServiceDataMessage to send them (we use ClosedGroupUpdateMessageSendJob)
|
// closedGroupUpdate is always null because we don't use SignalServiceDataMessage to send them (we use ClosedGroupUpdateMessageSendJob)
|
||||||
return new SignalServiceDataMessage(timestamp, group, attachments, body, expiresInSeconds, expirationUpdate, profileKey, quote, sharedContacts, previews,
|
return new SignalServiceDataMessage(timestamp, group, attachments, body, expiresInSeconds, expirationUpdate, profileKey, quote, sharedContacts, previews,
|
||||||
null, syncTarget);
|
null, syncTarget);
|
||||||
|
Loading…
Reference in New Issue
Block a user