pull/503/head
Niels Andriesse 3 years ago
parent bc66c45bca
commit 676c307412

@ -30,7 +30,6 @@ interface MessageDataProvider {
fun updateAttachmentAfterUploadSucceeded(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: DotNetAPI.UploadResult)
fun updateAttachmentAfterUploadFailed(attachmentId: Long)
// Quotes
fun getMessageForQuote(timestamp: Long, author: Address): Pair<Long, Boolean>?
fun getAttachmentsAndLinkPreviewFor(mmsId: Long): List<Attachment>
fun getMessageBodyFor(timestamp: Long, author: String): String

@ -1,6 +1,5 @@
package org.session.libsession.messaging.avatars;
import android.content.Context;
import androidx.annotation.NonNull;

@ -1,6 +1,5 @@
package org.session.libsession.messaging.avatars;
import android.content.Context;
import android.net.Uri;
@ -19,5 +18,4 @@ public interface ContactPhoto extends Key {
@Nullable Uri getUri(@NonNull Context context);
boolean isProfilePhoto();
}

@ -7,5 +7,4 @@ public interface FallbackContactPhoto {
public Drawable asDrawable(Context context, int color);
public Drawable asDrawable(Context context, int color, boolean inverted);
}

@ -19,7 +19,6 @@ import org.session.libsession.utilities.ViewUtil;
import java.util.regex.Pattern;
public class GeneratedContactPhoto implements FallbackContactPhoto {
private static final Pattern PATTERN = Pattern.compile("[^\\p{L}\\p{Nd}\\p{S}]+");

@ -1,6 +1,5 @@
package org.session.libsession.messaging.avatars;
import android.content.Context;
import android.net.Uri;

@ -1,6 +1,5 @@
package org.session.libsession.messaging.avatars;
import android.content.Context;
import android.net.Uri;

@ -1,6 +1,5 @@
package org.session.libsession.messaging.avatars;
import android.content.Context;
import android.net.Uri;

@ -11,13 +11,10 @@ import java.io.File
import java.io.FileInputStream
class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long): Job {
override var delegate: JobDelegate? = null
override var id: String? = null
override var failureCount: Int = 0
private val MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024
// Error
internal sealed class Error(val description: String) : Exception(description) {
object NoAttachment : Error("No such attachment.")
@ -28,17 +25,17 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
companion object {
val KEY: String = "AttachmentDownloadJob"
//keys used for database storage purpose
// Keys used for database storage
private val KEY_ATTACHMENT_ID = "attachment_id"
private val KEY_TS_INCOMING_MESSAGE_ID = "tsIncoming_message_id"
}
override fun execute() {
val handleFailure: (java.lang.Exception) -> Unit = { exception ->
if(exception is Error && exception == Error.NoAttachment) {
if (exception == Error.NoAttachment) {
MessagingConfiguration.shared.messageDataProvider.setAttachmentState(AttachmentState.FAILED, attachmentID, databaseMessageID)
this.handlePermanentFailure(exception)
} else if (exception is DotNetAPI.Error && exception == DotNetAPI.Error.ParsingFailed) {
} else if (exception == DotNetAPI.Error.ParsingFailed) {
// No need to retry if the response is invalid. Most likely this means we (incorrectly)
// got a "Cannot GET ..." error from the file server.
MessagingConfiguration.shared.messageDataProvider.setAttachmentState(AttachmentState.FAILED, attachmentID, databaseMessageID)
@ -53,9 +50,7 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
messageDataProvider.setAttachmentState(AttachmentState.STARTED, attachmentID, this.databaseMessageID)
val tempFile = createTempFile()
FileServerAPI.shared.downloadFile(tempFile, attachment.url, MAX_ATTACHMENT_SIZE, null)
// DECRYPTION
FileServerAPI.shared.downloadFile(tempFile, attachment.url, null)
// Assume we're retrieving an attachment for an open group server if the digest is not set
val stream = if (attachment.digest?.size ?: 0 == 0 || attachment.key.isNullOrEmpty()) FileInputStream(tempFile)
@ -89,12 +84,10 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
return file
}
//database functions
override fun serialize(): Data {
return Data.Builder().putLong(KEY_ATTACHMENT_ID, attachmentID)
.putLong(KEY_TS_INCOMING_MESSAGE_ID, databaseMessageID)
.build();
.putLong(KEY_TS_INCOMING_MESSAGE_ID, databaseMessageID)
.build();
}
override fun getFactoryKey(): String {
@ -102,6 +95,7 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
}
class Factory: Job.Factory<AttachmentDownloadJob> {
override fun create(data: Data): AttachmentDownloadJob {
return AttachmentDownloadJob(data.getLong(KEY_ATTACHMENT_ID), data.getLong(KEY_TS_INCOMING_MESSAGE_ID))
}

@ -18,7 +18,6 @@ import org.session.libsignal.service.loki.utilities.PlaintextOutputStreamFactory
import org.session.libsignal.utilities.logging.Log
class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val message: Message, val messageSendJobID: String) : Job {
override var delegate: JobDelegate? = null
override var id: String? = null
override var failureCount: Int = 0
@ -34,9 +33,7 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
val TAG = AttachmentUploadJob::class.simpleName
val KEY: String = "AttachmentUploadJob"
val maxFailureCount: Int = 20
//keys used for database storage purpose
// Keys used for database storage
private val KEY_ATTACHMENT_ID = "attachment_id"
private val KEY_THREAD_ID = "thread_id"
private val KEY_MESSAGE = "message"
@ -46,16 +43,12 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
override fun execute() {
try {
val attachment = MessagingConfiguration.shared.messageDataProvider.getScaledSignalAttachmentStream(attachmentID)
?: return handleFailure(Error.NoAttachment)
?: return handleFailure(Error.NoAttachment)
var server = FileServerAPI.shared.server
var shouldEncrypt = true
val usePadding = false
val openGroup = MessagingConfiguration.shared.storage.getOpenGroup(threadID)
openGroup?.let {
server = it.server
shouldEncrypt = false
}
val server = if (openGroup != null) openGroup.server else FileServerAPI.shared.server
val shouldEncrypt = (openGroup == null) // Encrypt if this isn't an open group
val attachmentKey = Util.getSecretBytes(64)
val paddedLength = if (usePadding) PaddingInputStream.getPaddedSize(attachment.length) else attachment.length
@ -67,9 +60,8 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
val uploadResult = FileServerAPI.shared.uploadAttachment(server, attachmentData)
handleSuccess(attachment, attachmentKey, uploadResult)
} catch (e: java.lang.Exception) {
if (e is Error && e == Error.NoAttachment) {
if (e == Error.NoAttachment) {
this.handlePermanentFailure(e)
} else if (e is DotNetAPI.Error && !e.isRetryable) {
this.handlePermanentFailure(e)
@ -77,7 +69,6 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
this.handleFailure(e)
}
}
}
private fun handleSuccess(attachment: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: DotNetAPI.UploadResult) {
@ -97,7 +88,7 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
private fun handleFailure(e: Exception) {
Log.w(TAG, "Attachment upload failed due to error: $this.")
delegate?.handleJobFailed(this, e)
if (failureCount + 1 == AttachmentUploadJob.maxFailureCount) {
if (failureCount + 1 == maxFailureCount) {
failAssociatedMessageSendJob(e)
}
}
@ -111,10 +102,7 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
}
}
//database functions
override fun serialize(): Data {
//serialize Message property
val kryo = Kryo()
kryo.isRegistrationRequired = false
val serializedMessage = ByteArray(4096)
@ -122,10 +110,10 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
kryo.writeObject(output, message)
output.close()
return Data.Builder().putLong(KEY_ATTACHMENT_ID, attachmentID)
.putString(KEY_THREAD_ID, threadID)
.putByteArray(KEY_MESSAGE, serializedMessage)
.putString(KEY_MESSAGE_SEND_JOB_ID, messageSendJobID)
.build();
.putString(KEY_THREAD_ID, threadID)
.putByteArray(KEY_MESSAGE, serializedMessage)
.putString(KEY_MESSAGE_SEND_JOB_ID, messageSendJobID)
.build();
}
override fun getFactoryKey(): String {
@ -133,9 +121,9 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
}
class Factory: Job.Factory<AttachmentUploadJob> {
override fun create(data: Data): AttachmentUploadJob {
val serializedMessage = data.getByteArray(KEY_MESSAGE)
//deserialize Message property
val kryo = Kryo()
val input = Input(serializedMessage)
val message: Message = kryo.readObject(input, Message::class.java)

@ -12,7 +12,6 @@ import org.session.libsession.utilities.ParcelableUtil;
import java.util.HashMap;
import java.util.Map;
// TODO AC: For now parcelable objects utilize byteArrays field to store their data into.
// Introduce a dedicated Map<String, byte[]> field specifically for parcelable needs.
public class Data {

@ -8,15 +8,13 @@ interface Job {
val maxFailureCount: Int
companion object {
//keys used for database storage purpose
// Keys used for database storage
private val KEY_ID = "id"
private val KEY_FAILURE_COUNT = "failure_count"
}
fun execute()
//database functions
fun serialize(): Data
/**

@ -1,6 +1,7 @@
package org.session.libsession.messaging.jobs
interface JobDelegate {
fun handleJobSucceeded(job: Job)
fun handleJobFailed(job: Job, error: Exception)
fun handleJobFailedPermanently(job: Job, error: Exception)

@ -14,19 +14,17 @@ import kotlin.math.min
import kotlin.math.pow
import kotlin.math.roundToLong
class JobQueue : JobDelegate {
private var hasResumedPendingJobs = false // Just for debugging
private val jobTimestampMap = ConcurrentHashMap<Long, AtomicInteger>()
private val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
private val scope = GlobalScope + SupervisorJob()
private val queue = Channel<Job>(UNLIMITED)
val timer = Timer()
init {
// process jobs
// Process jobs
scope.launch(dispatcher) {
while (isActive) {
queue.receive().let { job ->
@ -49,7 +47,7 @@ class JobQueue : JobDelegate {
private fun addWithoutExecuting(job: Job) {
// When adding multiple jobs in rapid succession, timestamps might not be good enough as a unique ID. To
// deal with this we keep track of the number of jobs with a given timestamp and that to the end of the
// deal with this we keep track of the number of jobs with a given timestamp and add that to the end of the
// timestamp to make it a unique ID. We can't use a random number because we do still want to keep track
// of the order in which the jobs were added.
val currentTime = System.currentTimeMillis()
@ -70,7 +68,7 @@ class JobQueue : JobDelegate {
val allPendingJobs = MessagingConfiguration.shared.storage.getAllPendingJobs(type)
allPendingJobs.sortedBy { it.id }.forEach { job ->
Log.i("Jobs", "Resuming pending job of type: ${job::class.simpleName}.")
queue.offer(job) // offer always called on unlimited capacity
queue.offer(job) // Offer always called on unlimited capacity
}
}
}

@ -7,7 +7,6 @@ import org.session.libsession.messaging.sending_receiving.handle
import org.session.libsignal.utilities.logging.Log
class MessageReceiveJob(val data: ByteArray, val isBackgroundPoll: Boolean, val openGroupMessageServerID: Long? = null, val openGroupID: String? = null) : Job {
override var delegate: JobDelegate? = null
override var id: String? = null
override var failureCount: Int = 0
@ -20,7 +19,7 @@ class MessageReceiveJob(val data: ByteArray, val isBackgroundPoll: Boolean, val
private val RECEIVE_LOCK = Object()
//keys used for database storage purpose
// Keys used for database storage
private val KEY_DATA = "data"
private val KEY_IS_BACKGROUND_POLL = "is_background_poll"
private val KEY_OPEN_GROUP_MESSAGE_SERVER_ID = "openGroupMessageServerID"
@ -68,8 +67,6 @@ class MessageReceiveJob(val data: ByteArray, val isBackgroundPoll: Boolean, val
delegate?.handleJobFailed(this, e)
}
//database functions
override fun serialize(): Data {
val builder = Data.Builder().putByteArray(KEY_DATA, data)
.putBoolean(KEY_IS_BACKGROUND_POLL, isBackgroundPoll)
@ -83,6 +80,7 @@ class MessageReceiveJob(val data: ByteArray, val isBackgroundPoll: Boolean, val
}
class Factory: Job.Factory<MessageReceiveJob> {
override fun create(data: Data): MessageReceiveJob {
return MessageReceiveJob(data.getByteArray(KEY_DATA), data.getBoolean(KEY_IS_BACKGROUND_POLL), data.getLong(KEY_OPEN_GROUP_MESSAGE_SERVER_ID), data.getString(KEY_OPEN_GROUP_ID))
}

@ -11,7 +11,6 @@ import org.session.libsession.messaging.sending_receiving.MessageSender
import org.session.libsignal.utilities.logging.Log
class MessageSendJob(val message: Message, val destination: Destination) : Job {
override var delegate: JobDelegate? = null
override var id: String? = null
override var failureCount: Int = 0
@ -22,7 +21,7 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
val TAG = MessageSendJob::class.simpleName
val KEY: String = "MessageSendJob"
//keys used for database storage purpose
// Keys used for database storage
private val KEY_MESSAGE = "message"
private val KEY_DESTINATION = "destination"
}
@ -77,10 +76,7 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
delegate?.handleJobFailed(this, error)
}
//database functions
override fun serialize(): Data {
//serialize Message and Destination properties
val kryo = Kryo()
kryo.isRegistrationRequired = false
val output = Output(ByteArray(4096), -1) // maxBufferSize '-1' will dynamically grow internally if we run out of room serializing the message
@ -92,8 +88,8 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
output.close()
val serializedDestination = output.toBytes()
return Data.Builder().putByteArray(KEY_MESSAGE, serializedMessage)
.putByteArray(KEY_DESTINATION, serializedDestination)
.build();
.putByteArray(KEY_DESTINATION, serializedDestination)
.build();
}
override fun getFactoryKey(): String {
@ -101,10 +97,10 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
}
class Factory: Job.Factory<MessageSendJob> {
override fun create(data: Data): MessageSendJob {
val serializedMessage = data.getByteArray(KEY_MESSAGE)
val serializedDestination = data.getByteArray(KEY_DESTINATION)
//deserialize Message and Destination properties
val kryo = Kryo()
var input = Input(serializedMessage)
val message = kryo.readClassAndObject(input) as Message

@ -26,7 +26,7 @@ class NotifyPNServerJob(val message: SnodeMessage) : Job {
companion object {
val KEY: String = "NotifyPNServerJob"
//keys used for database storage purpose
// Keys used for database storage
private val KEY_MESSAGE = "message"
}
@ -61,18 +61,14 @@ class NotifyPNServerJob(val message: SnodeMessage) : Job {
delegate?.handleJobFailed(this, error)
}
//database functions
override fun serialize(): Data {
//serialize SnodeMessage property
val kryo = Kryo()
kryo.isRegistrationRequired = false
val serializedMessage = ByteArray(4096)
val output = Output(serializedMessage)
kryo.writeObject(output, message)
output.close()
return Data.Builder().putByteArray(KEY_MESSAGE, serializedMessage)
.build();
return Data.Builder().putByteArray(KEY_MESSAGE, serializedMessage).build();
}
override fun getFactoryKey(): String {
@ -80,9 +76,9 @@ class NotifyPNServerJob(val message: SnodeMessage) : Job {
}
class Factory: Job.Factory<NotifyPNServerJob> {
override fun create(data: Data): NotifyPNServerJob {
val serializedMessage = data.getByteArray(KEY_MESSAGE)
//deserialize SnodeMessage property
val kryo = Kryo()
val input = Input(serializedMessage)
val message: SnodeMessage = kryo.readObject(input, SnodeMessage::class.java)

@ -1,7 +1,5 @@
package org.session.libsession.messaging.jobs
import java.util.*
class SessionJobInstantiator(private val jobFactories: Map<String, Job.Factory<out Job>>) {
fun instantiate(jobFactoryKey: String, data: Data): Job {

@ -1,7 +1,5 @@
package org.session.libsession.messaging.jobs
import java.util.*
class SessionJobManagerFactories {
companion object {

@ -28,15 +28,10 @@ class ClosedGroupControlMessage() : ControlMessage() {
var kind: Kind? = null
// Kind enum
sealed class Kind {
class New(var publicKey: ByteString, var name: String, var encryptionKeyPair: ECKeyPair?, var members: List<ByteString>, var admins: List<ByteString>) : Kind() {
internal constructor(): this(ByteString.EMPTY, "", null, listOf(), listOf())
}
/// - Note: Deprecated in favor of more explicit group updates.
class Update(var name: String, var members: List<ByteString>) : Kind() {
internal constructor(): this("", listOf())
}
/// An encryption key pair encrypted for each member individually.
///
/// - Note: `publicKey` is only set when an encryption key pair is sent in a one-to-one context (i.e. not in a group).
@ -53,18 +48,15 @@ class ClosedGroupControlMessage() : ControlMessage() {
internal constructor(): this(listOf())
}
class MemberLeft() : Kind()
class EncryptionKeyPairRequest(): Kind()
val description: String =
when(this) {
is New -> "new"
is Update -> "update"
is EncryptionKeyPair -> "encryptionKeyPair"
is NameChange -> "nameChange"
is MembersAdded -> "membersAdded"
is MembersRemoved -> "membersRemoved"
is MemberLeft -> "memberLeft"
is EncryptionKeyPairRequest -> "encryptionKeyPairRequest"
}
}
@ -75,12 +67,11 @@ class ClosedGroupControlMessage() : ControlMessage() {
if (!proto.hasDataMessage() || !proto.dataMessage.hasClosedGroupControlMessage()) return null
val closedGroupControlMessageProto = proto.dataMessage?.closedGroupControlMessage!!
val kind: Kind
when(closedGroupControlMessageProto.type) {
when (closedGroupControlMessageProto.type) {
DataMessage.ClosedGroupControlMessage.Type.NEW -> {
val publicKey = closedGroupControlMessageProto.publicKey ?: return null
val name = closedGroupControlMessageProto.name ?: return null
val encryptionKeyPairAsProto = closedGroupControlMessageProto.encryptionKeyPair ?: return null
try {
val encryptionKeyPair = ECKeyPair(DjbECPublicKey(encryptionKeyPairAsProto.publicKey.toByteArray()), DjbECPrivateKey(encryptionKeyPairAsProto.privateKey.toByteArray()))
kind = Kind.New(publicKey, name, encryptionKeyPair, closedGroupControlMessageProto.membersList, closedGroupControlMessageProto.adminsList)
@ -89,10 +80,6 @@ class ClosedGroupControlMessage() : ControlMessage() {
return null
}
}
DataMessage.ClosedGroupControlMessage.Type.UPDATE -> {
val name = closedGroupControlMessageProto.name ?: return null
kind = Kind.Update(name, closedGroupControlMessageProto.membersList)
}
DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR -> {
val publicKey = closedGroupControlMessageProto.publicKey
val wrappers = closedGroupControlMessageProto.wrappersList.mapNotNull { KeyPairWrapper.fromProto(it) }
@ -111,35 +98,28 @@ class ClosedGroupControlMessage() : ControlMessage() {
DataMessage.ClosedGroupControlMessage.Type.MEMBER_LEFT -> {
kind = Kind.MemberLeft()
}
DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR_REQUEST -> {
kind = Kind.EncryptionKeyPairRequest()
}
}
return ClosedGroupControlMessage(kind)
}
}
// constructor
internal constructor(kind: Kind?) : this() {
this.kind = kind
}
// validation
override fun isValid(): Boolean {
if (!super.isValid()) return false
val kind = kind ?: return false
return when(kind) {
is Kind.New -> {
!kind.publicKey.isEmpty && kind.name.isNotEmpty() && kind.encryptionKeyPair!!.publicKey != null
&& kind.encryptionKeyPair!!.privateKey != null && kind.members.isNotEmpty() && kind.admins.isNotEmpty()
&& kind.encryptionKeyPair!!.privateKey != null && kind.members.isNotEmpty() && kind.admins.isNotEmpty()
}
is Kind.Update -> kind.name.isNotEmpty()
is Kind.EncryptionKeyPair -> true
is Kind.NameChange -> kind.name.isNotEmpty()
is Kind.MembersAdded -> kind.members.isNotEmpty()
is Kind.MembersRemoved -> kind.members.isNotEmpty()
is Kind.MemberLeft -> true
is Kind.EncryptionKeyPairRequest -> true
}
}
@ -163,11 +143,6 @@ class ClosedGroupControlMessage() : ControlMessage() {
closedGroupControlMessage.addAllMembers(kind.members)
closedGroupControlMessage.addAllAdmins(kind.admins)
}
is Kind.Update -> {
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.UPDATE
closedGroupControlMessage.name = kind.name
closedGroupControlMessage.addAllMembers(kind.members)
}
is Kind.EncryptionKeyPair -> {
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR
closedGroupControlMessage.publicKey = kind.publicKey
@ -188,9 +163,6 @@ class ClosedGroupControlMessage() : ControlMessage() {
is Kind.MemberLeft -> {
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.MEMBER_LEFT
}
is Kind.EncryptionKeyPairRequest -> {
// TODO: closedGroupControlMessage.type = SignalServiceProtos.ClosedGroupUpdateV2.Type.ENCRYPTION_KEY_PAIR_REQUEST
}
}
val contentProto = SignalServiceProtos.Content.newBuilder()
val dataMessageProto = DataMessage.newBuilder()
@ -199,7 +171,7 @@ class ClosedGroupControlMessage() : ControlMessage() {
setGroupContext(dataMessageProto)
// Expiration timer
// TODO: We * want * expiration timer updates to be explicit. But currently Android will disable the expiration timer for a conversation
// if it receives a message without the current expiration timer value attached to it...
// if it receives a message without the current expiration timer value attached to it...
dataMessageProto.expireTimer = Recipient.from(MessagingConfiguration.shared.context, Address.fromSerialized(GroupUtil.doubleEncodeGroupID(recipient!!)), false).expireMessages
contentProto.dataMessage = dataMessageProto.build()
return contentProto.build()

@ -185,14 +185,14 @@ open class DotNetAPI {
/**
* Blocks the calling thread.
*/
fun downloadFile(destination: File, url: String, maxSize: Int, listener: SignalServiceAttachment.ProgressListener?) {
fun downloadFile(destination: File, url: String, listener: SignalServiceAttachment.ProgressListener?) {
val outputStream = FileOutputStream(destination) // Throws
var remainingAttempts = 4
var exception: Exception? = null
while (remainingAttempts > 0) {
remainingAttempts -= 1
try {
downloadFile(outputStream, url, maxSize, listener)
downloadFile(outputStream, url, listener)
exception = null
break
} catch (e: Exception) {
@ -205,7 +205,7 @@ open class DotNetAPI {
/**
* Blocks the calling thread.
*/
fun downloadFile(outputStream: OutputStream, url: String, maxSize: Int, listener: SignalServiceAttachment.ProgressListener?) {
fun downloadFile(outputStream: OutputStream, url: String, listener: SignalServiceAttachment.ProgressListener?) {
// We need to throw a PushNetworkException or NonSuccessfulResponseCodeException
// because the underlying Signal logic requires these to work correctly
val oldPrefixedHost = "https://" + HttpUrl.get(url).host()
@ -228,7 +228,7 @@ open class DotNetAPI {
throw PushNetworkException("Missing response body.")
}
val body = Base64.decode(result)
if (body.size > maxSize) {
if (body.size > FileServerAPI.maxFileSize) {
Log.d("Loki", "Attachment size limit exceeded.")
throw PushNetworkException("Max response size exceeded.")
}
@ -239,7 +239,7 @@ open class DotNetAPI {
while (bytes >= 0) {
outputStream.write(buffer, 0, bytes)
count += bytes
if (count > maxSize) {
if (count > FileServerAPI.maxFileSize) {
Log.d("Loki", "Attachment size limit exceeded.")
throw PushNetworkException("Max response size exceeded.")
}

@ -1,6 +1,5 @@
package org.session.libsession.utilities;
import java.util.concurrent.LinkedBlockingDeque;
public class LinkedBlockingLifoQueue<E> extends LinkedBlockingDeque<E> {

@ -3,7 +3,6 @@ package org.session.libsession.utilities
import android.telephony.PhoneNumberUtils
import android.util.Patterns
object NumberUtil {
private val emailPattern = Patterns.EMAIL_ADDRESS

@ -14,6 +14,7 @@ class SSKEnvironment(
val notificationManager: MessageNotifier,
val messageExpirationManager: MessageExpirationManagerProtocol
) {
interface TypingIndicatorsProtocol {
fun didReceiveTypingStartedMessage(context: Context, threadId: Long, author: Address, device: Int)
fun didReceiveTypingStoppedMessage(context: Context, threadId: Long, author: Address, device: Int, isReplacedByIncomingMessage: Boolean)

@ -5,6 +5,7 @@ import org.session.libsignal.utilities.concurrent.ListenableFuture.Listener;
import java.util.concurrent.ExecutionException;
public abstract class AssertedSuccessListener<T> implements Listener<T> {
@Override
public void onFailure(ExecutionException e) {
throw new AssertionError(e);

@ -30,5 +30,4 @@ public final class DynamicLanguageContextWrapper {
copy.setLocale(locale);
return copy;
}
}

@ -7,8 +7,7 @@ import java.util.Locale;
public final class LanguageString {
private LanguageString() {
}
private LanguageString() { }
/**
* @param languageString String in format language_REGION, e.g. en_US

@ -1,6 +1,5 @@
package org.session.libsession.utilities.preferences;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

@ -10,7 +10,6 @@ public abstract class SnackbarAsyncTask<Params>
extends AsyncTask<Params, Void, Void>
implements View.OnClickListener
{
private final View view;
private final String snackbarText;
private final String snackbarActionText;

@ -1,6 +1,5 @@
package org.session.libsession.utilities.views;
import android.view.ViewStub;
import androidx.annotation.NonNull;

Loading…
Cancel
Save