mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-23 18:15:22 +00:00
Merge remote-tracking branch 'upstream/dev' into feature/unread-mention-indicator
# Conflicts: # app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt # libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt
This commit is contained in:
commit
dc9458f313
@ -47,6 +47,7 @@ import org.session.libsession.utilities.Util;
|
|||||||
import org.session.libsession.utilities.WindowDebouncer;
|
import org.session.libsession.utilities.WindowDebouncer;
|
||||||
import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageContextWrapper;
|
import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageContextWrapper;
|
||||||
import org.session.libsession.utilities.dynamiclanguage.LocaleParser;
|
import org.session.libsession.utilities.dynamiclanguage.LocaleParser;
|
||||||
|
import org.session.libsignal.utilities.HTTP;
|
||||||
import org.session.libsignal.utilities.JsonUtil;
|
import org.session.libsignal.utilities.JsonUtil;
|
||||||
import org.session.libsignal.utilities.Log;
|
import org.session.libsignal.utilities.Log;
|
||||||
import org.session.libsignal.utilities.ThreadUtils;
|
import org.session.libsignal.utilities.ThreadUtils;
|
||||||
@ -67,6 +68,7 @@ import org.thoughtcrime.securesms.groups.OpenGroupMigrator;
|
|||||||
import org.thoughtcrime.securesms.home.HomeActivity;
|
import org.thoughtcrime.securesms.home.HomeActivity;
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
|
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.jobs.FastJobStorage;
|
import org.thoughtcrime.securesms.jobs.FastJobStorage;
|
||||||
import org.thoughtcrime.securesms.jobs.JobManagerFactories;
|
import org.thoughtcrime.securesms.jobs.JobManagerFactories;
|
||||||
import org.thoughtcrime.securesms.logging.AndroidLogger;
|
import org.thoughtcrime.securesms.logging.AndroidLogger;
|
||||||
@ -237,6 +239,9 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
|
|||||||
resubmitProfilePictureIfNeeded();
|
resubmitProfilePictureIfNeeded();
|
||||||
loadEmojiSearchIndexIfNeeded();
|
loadEmojiSearchIndexIfNeeded();
|
||||||
EmojiSource.refresh();
|
EmojiSource.refresh();
|
||||||
|
|
||||||
|
NetworkConstraint networkConstraint = new NetworkConstraint.Factory(this).create();
|
||||||
|
HTTP.INSTANCE.setConnectedToNetwork(networkConstraint::isMet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -176,6 +176,11 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper)
|
|||||||
return messageDB.getMessageID(serverId, threadId)
|
return messageDB.getMessageID(serverId, threadId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getMessageIDs(serverIds: List<Long>, threadId: Long): Pair<List<Long>, List<Long>> {
|
||||||
|
val messageDB = DatabaseComponent.get(context).lokiMessageDatabase()
|
||||||
|
return messageDB.getMessageIDs(serverIds, threadId)
|
||||||
|
}
|
||||||
|
|
||||||
override fun deleteMessage(messageID: Long, isSms: Boolean) {
|
override fun deleteMessage(messageID: Long, isSms: Boolean) {
|
||||||
val messagingDatabase: MessagingDatabase = if (isSms) DatabaseComponent.get(context).smsDatabase()
|
val messagingDatabase: MessagingDatabase = if (isSms) DatabaseComponent.get(context).smsDatabase()
|
||||||
else DatabaseComponent.get(context).mmsDatabase()
|
else DatabaseComponent.get(context).mmsDatabase()
|
||||||
@ -184,6 +189,15 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper)
|
|||||||
DatabaseComponent.get(context).lokiMessageDatabase().deleteMessageServerHash(messageID)
|
DatabaseComponent.get(context).lokiMessageDatabase().deleteMessageServerHash(messageID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun deleteMessages(messageIDs: List<Long>, threadId: Long, isSms: Boolean) {
|
||||||
|
val messagingDatabase: MessagingDatabase = if (isSms) DatabaseComponent.get(context).smsDatabase()
|
||||||
|
else DatabaseComponent.get(context).mmsDatabase()
|
||||||
|
|
||||||
|
messagingDatabase.deleteMessages(messageIDs.toLongArray(), threadId)
|
||||||
|
DatabaseComponent.get(context).lokiMessageDatabase().deleteMessages(messageIDs)
|
||||||
|
DatabaseComponent.get(context).lokiMessageDatabase().deleteMessageServerHashes(messageIDs)
|
||||||
|
}
|
||||||
|
|
||||||
override fun updateMessageAsDeleted(timestamp: Long, author: String): Long? {
|
override fun updateMessageAsDeleted(timestamp: Long, author: String): Long? {
|
||||||
val database = DatabaseComponent.get(context).mmsSmsDatabase()
|
val database = DatabaseComponent.get(context).mmsSmsDatabase()
|
||||||
val address = Address.fromSerialized(author)
|
val address = Address.fromSerialized(author)
|
||||||
|
@ -13,6 +13,11 @@ class ScreenshotObserver(private val context: Context, handler: Handler, private
|
|||||||
override fun onChange(selfChange: Boolean, uri: Uri?) {
|
override fun onChange(selfChange: Boolean, uri: Uri?) {
|
||||||
super.onChange(selfChange, uri)
|
super.onChange(selfChange, uri)
|
||||||
uri ?: return
|
uri ?: return
|
||||||
|
|
||||||
|
// There is an odd bug where we can get notified for changes to 'content://media/external'
|
||||||
|
// directly which is a protected folder, this code is to prevent that crash
|
||||||
|
if (uri.scheme == "content" && uri.host == "media" && uri.path == "/external") { return }
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
queryRelativeDataColumn(uri)
|
queryRelativeDataColumn(uri)
|
||||||
} else {
|
} else {
|
||||||
|
@ -315,13 +315,26 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
restoreDraftIfNeeded()
|
restoreDraftIfNeeded()
|
||||||
setUpUiStateObserver()
|
setUpUiStateObserver()
|
||||||
binding!!.scrollToBottomButton.setOnClickListener {
|
binding!!.scrollToBottomButton.setOnClickListener {
|
||||||
val layoutManager = binding?.conversationRecyclerView?.layoutManager ?: return@setOnClickListener
|
val layoutManager = (binding?.conversationRecyclerView?.layoutManager as? LinearLayoutManager) ?: return@setOnClickListener
|
||||||
|
|
||||||
if (layoutManager.isSmoothScrolling) {
|
if (layoutManager.isSmoothScrolling) {
|
||||||
binding?.conversationRecyclerView?.scrollToPosition(0)
|
binding?.conversationRecyclerView?.scrollToPosition(0)
|
||||||
} else {
|
} else {
|
||||||
|
// It looks like 'smoothScrollToPosition' will actually load all intermediate items in
|
||||||
|
// order to do the scroll, this can be very slow if there are a lot of messages so
|
||||||
|
// instead we check the current position and if there are more than 10 items to scroll
|
||||||
|
// we jump instantly to the 10th item and scroll from there (this should happen quick
|
||||||
|
// enough to give a similar scroll effect without having to load everything)
|
||||||
|
val position = layoutManager.findFirstVisibleItemPosition()
|
||||||
|
if (position > 10) {
|
||||||
|
binding?.conversationRecyclerView?.scrollToPosition(10)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding?.conversationRecyclerView?.post {
|
||||||
binding?.conversationRecyclerView?.smoothScrollToPosition(0)
|
binding?.conversationRecyclerView?.smoothScrollToPosition(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
unreadCount = mmsSmsDb.getUnreadCount(viewModel.threadId)
|
unreadCount = mmsSmsDb.getUnreadCount(viewModel.threadId)
|
||||||
updateUnreadCountIndicator()
|
updateUnreadCountIndicator()
|
||||||
setUpTypingObserver()
|
setUpTypingObserver()
|
||||||
|
@ -35,6 +35,7 @@ import com.bumptech.glide.Glide;
|
|||||||
|
|
||||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
||||||
@ -318,6 +319,28 @@ public class AttachmentDatabase extends Database {
|
|||||||
notifyAttachmentListeners();
|
notifyAttachmentListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
|
void deleteAttachmentsForMessages(long[] mmsIds) {
|
||||||
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
|
Cursor cursor = null;
|
||||||
|
String mmsIdString = StringUtils.join(mmsIds, ',');
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = database.query(TABLE_NAME, new String[] {DATA, THUMBNAIL, CONTENT_TYPE}, MMS_ID + " IN (?)",
|
||||||
|
new String[] {mmsIdString}, null, null, null);
|
||||||
|
|
||||||
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
|
deleteAttachmentOnDisk(cursor.getString(0), cursor.getString(1), cursor.getString(2));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
database.delete(TABLE_NAME, MMS_ID + " IN (?)", new String[] {mmsIdString});
|
||||||
|
notifyAttachmentListeners();
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteAttachment(@NonNull AttachmentId id) {
|
public void deleteAttachment(@NonNull AttachmentId id) {
|
||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
|
|
||||||
|
@ -318,6 +318,19 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
|||||||
notifyConversationListListeners();
|
notifyConversationListListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasDownloadedProfilePicture(String groupId) {
|
||||||
|
try (Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[]{AVATAR}, GROUP_ID + " = ?",
|
||||||
|
new String[] {groupId},
|
||||||
|
null, null, null))
|
||||||
|
{
|
||||||
|
if (cursor != null && cursor.moveToNext()) {
|
||||||
|
return !cursor.isNull(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void updateMembers(String groupId, List<Address> members) {
|
public void updateMembers(String groupId, List<Address> members) {
|
||||||
Collections.sort(members);
|
Collections.sort(members);
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import androidx.annotation.NonNull;
|
|||||||
|
|
||||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.session.libsession.utilities.Address;
|
import org.session.libsession.utilities.Address;
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||||
|
|
||||||
@ -109,6 +110,11 @@ public class GroupReceiptDatabase extends Database {
|
|||||||
db.delete(TABLE_NAME, MMS_ID + " = ?", new String[] {String.valueOf(mmsId)});
|
db.delete(TABLE_NAME, MMS_ID + " = ?", new String[] {String.valueOf(mmsId)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deleteRowsForMessages(long[] mmsIds) {
|
||||||
|
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||||
|
db.delete(TABLE_NAME, MMS_ID + " IN (?)", new String[] {StringUtils.join(mmsIds, ',')});
|
||||||
|
}
|
||||||
|
|
||||||
void deleteAllRows() {
|
void deleteAllRows() {
|
||||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||||
db.delete(TABLE_NAME, null, null);
|
db.delete(TABLE_NAME, null, null);
|
||||||
|
@ -77,6 +77,25 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
|||||||
database.endTransaction()
|
database.endTransaction()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteMessages(messageIDs: List<Long>) {
|
||||||
|
val database = databaseHelper.writableDatabase
|
||||||
|
database.beginTransaction()
|
||||||
|
|
||||||
|
database.delete(
|
||||||
|
messageIDTable,
|
||||||
|
"${Companion.messageID} IN (${messageIDs.map { "?" }.joinToString(",")})",
|
||||||
|
messageIDs.map { "$it" }.toTypedArray()
|
||||||
|
)
|
||||||
|
database.delete(
|
||||||
|
messageThreadMappingTable,
|
||||||
|
"${Companion.messageID} IN (${messageIDs.map { "?" }.joinToString(",")})",
|
||||||
|
messageIDs.map { "$it" }.toTypedArray()
|
||||||
|
)
|
||||||
|
|
||||||
|
database.setTransactionSuccessful()
|
||||||
|
database.endTransaction()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return pair of sms or mms table-specific ID and whether it is in SMS table
|
* @return pair of sms or mms table-specific ID and whether it is in SMS table
|
||||||
*/
|
*/
|
||||||
@ -96,6 +115,37 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getMessageIDs(serverIDs: List<Long>, threadID: Long): Pair<List<Long>, List<Long>> {
|
||||||
|
val database = databaseHelper.readableDatabase
|
||||||
|
|
||||||
|
// Retrieve the message ids
|
||||||
|
val messageIdCursor = database
|
||||||
|
.rawQuery(
|
||||||
|
"""
|
||||||
|
SELECT ${messageThreadMappingTable}.${messageID}, ${messageIDTable}.${messageType}
|
||||||
|
FROM ${messageThreadMappingTable}
|
||||||
|
JOIN ${messageIDTable} ON ${messageIDTable}.message_id = ${messageThreadMappingTable}.${messageID}
|
||||||
|
WHERE (
|
||||||
|
${messageThreadMappingTable}.${Companion.threadID} = $threadID AND
|
||||||
|
${messageThreadMappingTable}.${Companion.serverID} IN (${serverIDs.joinToString(",")})
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
val smsMessageIds: MutableList<Long> = mutableListOf()
|
||||||
|
val mmsMessageIds: MutableList<Long> = mutableListOf()
|
||||||
|
while (messageIdCursor.moveToNext()) {
|
||||||
|
if (messageIdCursor.getInt(1) == SMS_TYPE) {
|
||||||
|
smsMessageIds.add(messageIdCursor.getLong(0))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mmsMessageIds.add(messageIdCursor.getLong(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pair(smsMessageIds, mmsMessageIds)
|
||||||
|
}
|
||||||
|
|
||||||
override fun setServerID(messageID: Long, serverID: Long, isSms: Boolean) {
|
override fun setServerID(messageID: Long, serverID: Long, isSms: Boolean) {
|
||||||
val database = databaseHelper.writableDatabase
|
val database = databaseHelper.writableDatabase
|
||||||
val contentValues = ContentValues(3)
|
val contentValues = ContentValues(3)
|
||||||
@ -183,6 +233,15 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
|||||||
database.delete(messageHashTable, "${Companion.messageID} = ?", arrayOf(messageID.toString()))
|
database.delete(messageHashTable, "${Companion.messageID} = ?", arrayOf(messageID.toString()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteMessageServerHashes(messageIDs: List<Long>) {
|
||||||
|
val database = databaseHelper.writableDatabase
|
||||||
|
database.delete(
|
||||||
|
messageHashTable,
|
||||||
|
"${Companion.messageID} IN (${messageIDs.map { "?" }.joinToString(",")})",
|
||||||
|
messageIDs.map { "$it" }.toTypedArray()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun migrateThreadId(legacyThreadId: Long, newThreadId: Long) {
|
fun migrateThreadId(legacyThreadId: Long, newThreadId: Long) {
|
||||||
val database = databaseHelper.writableDatabase
|
val database = databaseHelper.writableDatabase
|
||||||
val contentValues = ContentValues(1)
|
val contentValues = ContentValues(1)
|
||||||
|
@ -42,6 +42,7 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn
|
|||||||
public abstract void markAsDeleted(long messageId, boolean read, boolean hasMention);
|
public abstract void markAsDeleted(long messageId, boolean read, boolean hasMention);
|
||||||
|
|
||||||
public abstract boolean deleteMessage(long messageId);
|
public abstract boolean deleteMessage(long messageId);
|
||||||
|
public abstract boolean deleteMessages(long[] messageId, long threadId);
|
||||||
|
|
||||||
public abstract void updateThreadId(long fromId, long toId);
|
public abstract void updateThreadId(long fromId, long toId);
|
||||||
|
|
||||||
|
@ -999,6 +999,23 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
|||||||
return threadDeleted
|
return threadDeleted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun deleteMessages(messageIds: LongArray, threadId: Long): Boolean {
|
||||||
|
val attachmentDatabase = get(context).attachmentDatabase()
|
||||||
|
val groupReceiptDatabase = get(context).groupReceiptDatabase()
|
||||||
|
|
||||||
|
queue(Runnable { attachmentDatabase.deleteAttachmentsForMessages(messageIds) })
|
||||||
|
groupReceiptDatabase.deleteRowsForMessages(messageIds)
|
||||||
|
|
||||||
|
val database = databaseHelper.writableDatabase
|
||||||
|
database!!.delete(TABLE_NAME, ID_IN, arrayOf(messageIds.joinToString(",")))
|
||||||
|
|
||||||
|
val threadDeleted = get(context).threadDatabase().update(threadId, false)
|
||||||
|
notifyConversationListeners(threadId)
|
||||||
|
notifyStickerListeners()
|
||||||
|
notifyStickerPackListeners()
|
||||||
|
return threadDeleted
|
||||||
|
}
|
||||||
|
|
||||||
override fun updateThreadId(fromId: Long, toId: Long) {
|
override fun updateThreadId(fromId: Long, toId: Long) {
|
||||||
val contentValues = ContentValues(1)
|
val contentValues = ContentValues(1)
|
||||||
contentValues.put(THREAD_ID, toId)
|
contentValues.put(THREAD_ID, toId)
|
||||||
|
@ -31,6 +31,7 @@ import com.annimon.stream.Stream;
|
|||||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||||
import net.zetetic.database.sqlcipher.SQLiteStatement;
|
import net.zetetic.database.sqlcipher.SQLiteStatement;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.session.libsession.messaging.calls.CallMessageType;
|
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;
|
||||||
@ -52,6 +53,7 @@ import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -603,6 +605,30 @@ public class SmsDatabase extends MessagingDatabase {
|
|||||||
return threadDeleted;
|
return threadDeleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteMessages(long[] messageIds, long threadId) {
|
||||||
|
String[] argsArray = new String[messageIds.length];
|
||||||
|
String[] argValues = new String[messageIds.length];
|
||||||
|
Arrays.fill(argsArray, "?");
|
||||||
|
|
||||||
|
for (int i = 0; i < messageIds.length; i++) {
|
||||||
|
argValues[i] = (messageIds[i] + "");
|
||||||
|
}
|
||||||
|
|
||||||
|
String combinedMessageIdArgss = StringUtils.join(messageIds, ',');
|
||||||
|
String combinedMessageIds = StringUtils.join(messageIds, ',');
|
||||||
|
Log.i("MessageDatabase", "Deleting: " + combinedMessageIds);
|
||||||
|
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||||
|
db.delete(
|
||||||
|
TABLE_NAME,
|
||||||
|
ID + " IN (" + StringUtils.join(argsArray, ',') + ")",
|
||||||
|
argValues
|
||||||
|
);
|
||||||
|
boolean threadDeleted = DatabaseComponent.get(context).threadDatabase().update(threadId, false);
|
||||||
|
notifyConversationListeners(threadId);
|
||||||
|
return threadDeleted;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateThreadId(long fromId, long toId) {
|
public void updateThreadId(long fromId, long toId) {
|
||||||
ContentValues contentValues = new ContentValues(1);
|
ContentValues contentValues = new ContentValues(1);
|
||||||
|
@ -321,6 +321,10 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
|||||||
DatabaseComponent.get(context).groupDatabase().updateProfilePicture(groupID, newValue)
|
DatabaseComponent.get(context).groupDatabase().updateProfilePicture(groupID, newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun hasDownloadedProfilePicture(groupID: String): Boolean {
|
||||||
|
return DatabaseComponent.get(context).groupDatabase().hasDownloadedProfilePicture(groupID)
|
||||||
|
}
|
||||||
|
|
||||||
override fun getReceivedMessageTimestamps(): Set<Long> {
|
override fun getReceivedMessageTimestamps(): Set<Long> {
|
||||||
return SessionMetaProtocol.getTimestamps()
|
return SessionMetaProtocol.getTimestamps()
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package org.thoughtcrime.securesms.webrtc
|
package org.thoughtcrime.securesms.webrtc
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.telephony.TelephonyManager
|
import android.telephony.TelephonyManager
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
@ -176,8 +178,22 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va
|
|||||||
_callStateEvents.value = newState
|
_callStateEvents.value = newState
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isBusy(context: Context, callId: UUID) = callId != this.callId && (currentConnectionState != CallState.Idle
|
fun isBusy(context: Context, callId: UUID): Boolean {
|
||||||
|| context.getSystemService(TelephonyManager::class.java).callState != TelephonyManager.CALL_STATE_IDLE)
|
// Make sure we have the permission before accessing the callState
|
||||||
|
if (ContextCompat.checkSelfPermission(context, android.Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
return (
|
||||||
|
callId != this.callId && (
|
||||||
|
currentConnectionState != CallState.Idle ||
|
||||||
|
context.getSystemService(TelephonyManager::class.java).callState != TelephonyManager.CALL_STATE_IDLE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
callId != this.callId &&
|
||||||
|
currentConnectionState != CallState.Idle
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun isPreOffer() = currentConnectionState == CallState.RemotePreOffer
|
fun isPreOffer() = currentConnectionState == CallState.RemotePreOffer
|
||||||
|
|
||||||
|
@ -12,6 +12,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.utilities.WebRtcUtils
|
import org.session.libsession.messaging.utilities.WebRtcUtils
|
||||||
|
import org.session.libsession.snode.SnodeAPI
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
@ -29,6 +30,10 @@ import org.webrtc.IceCandidate
|
|||||||
|
|
||||||
class CallMessageProcessor(private val context: Context, private val textSecurePreferences: TextSecurePreferences, lifecycle: Lifecycle, private val storage: StorageProtocol) {
|
class CallMessageProcessor(private val context: Context, private val textSecurePreferences: TextSecurePreferences, lifecycle: Lifecycle, private val storage: StorageProtocol) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val VERY_EXPIRED_TIME = 15 * 60 * 1000L
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
lifecycle.coroutineScope.launch(IO) {
|
lifecycle.coroutineScope.launch(IO) {
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
@ -53,6 +58,13 @@ class CallMessageProcessor(private val context: Context, private val textSecureP
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val isVeryExpired = (nextMessage.sentTimestamp?:0) + VERY_EXPIRED_TIME < SnodeAPI.nowWithOffset
|
||||||
|
if (isVeryExpired) {
|
||||||
|
Log.e("Loki", "Dropping very expired call message")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
when (nextMessage.type) {
|
when (nextMessage.type) {
|
||||||
OFFER -> incomingCall(nextMessage)
|
OFFER -> incomingCall(nextMessage)
|
||||||
ANSWER -> incomingAnswer(nextMessage)
|
ANSWER -> incomingAnswer(nextMessage)
|
||||||
@ -78,7 +90,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)
|
||||||
ContextCompat.startForegroundService(context, hangupIntent)
|
context.startService(hangupIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun incomingAnswer(callMessage: CallMessage) {
|
private fun incomingAnswer(callMessage: CallMessage) {
|
||||||
@ -91,7 +103,7 @@ class CallMessageProcessor(private val context: Context, private val textSecureP
|
|||||||
sdp = sdp,
|
sdp = sdp,
|
||||||
callId = callId
|
callId = callId
|
||||||
)
|
)
|
||||||
ContextCompat.startForegroundService(context, answerIntent)
|
context.startService(answerIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleIceCandidates(callMessage: CallMessage) {
|
private fun handleIceCandidates(callMessage: CallMessage) {
|
||||||
@ -120,7 +132,7 @@ class CallMessageProcessor(private val context: Context, private val textSecureP
|
|||||||
callId = callId,
|
callId = callId,
|
||||||
callTime = callMessage.sentTimestamp!!
|
callTime = callMessage.sentTimestamp!!
|
||||||
)
|
)
|
||||||
ContextCompat.startForegroundService(context, incomingIntent)
|
context.startService(incomingIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun incomingCall(callMessage: CallMessage) {
|
private fun incomingCall(callMessage: CallMessage) {
|
||||||
@ -134,8 +146,7 @@ class CallMessageProcessor(private val context: Context, private val textSecureP
|
|||||||
callId = callId,
|
callId = callId,
|
||||||
callTime = callMessage.sentTimestamp!!
|
callTime = callMessage.sentTimestamp!!
|
||||||
)
|
)
|
||||||
ContextCompat.startForegroundService(context, incomingIntent)
|
context.startService(incomingIntent)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CallMessage.iceCandidates(): List<IceCandidate> {
|
private fun CallMessage.iceCandidates(): List<IceCandidate> {
|
||||||
|
@ -865,5 +865,5 @@
|
|||||||
<string name="new_conversation_dialog_back_button_content_description">Navigate Back</string>
|
<string name="new_conversation_dialog_back_button_content_description">Navigate Back</string>
|
||||||
<string name="new_conversation_dialog_close_button_content_description">Close Dialog</string>
|
<string name="new_conversation_dialog_close_button_content_description">Close Dialog</string>
|
||||||
<string name="ErrorNotifier_migration">Database Upgrade Failed</string>
|
<string name="ErrorNotifier_migration">Database Upgrade Failed</string>
|
||||||
<string name="ErrorNotifier_migration_downgrade">Please contact support to report the error and then install an older version to continue using Session.</string>
|
<string name="ErrorNotifier_migration_downgrade">Please contact support to report the error.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -20,7 +20,9 @@ interface MessageDataProvider {
|
|||||||
* @return pair of sms or mms table-specific ID and whether it is in SMS table
|
* @return pair of sms or mms table-specific ID and whether it is in SMS table
|
||||||
*/
|
*/
|
||||||
fun getMessageID(serverId: Long, threadId: Long): Pair<Long, Boolean>?
|
fun getMessageID(serverId: Long, threadId: Long): Pair<Long, Boolean>?
|
||||||
|
fun getMessageIDs(serverIDs: List<Long>, threadID: Long): Pair<List<Long>, List<Long>>
|
||||||
fun deleteMessage(messageID: Long, isSms: Boolean)
|
fun deleteMessage(messageID: Long, isSms: Boolean)
|
||||||
|
fun deleteMessages(messageIDs: List<Long>, threadId: Long, isSms: Boolean)
|
||||||
fun updateMessageAsDeleted(timestamp: Long, author: String): Long?
|
fun updateMessageAsDeleted(timestamp: Long, author: String): Long?
|
||||||
fun getServerHashForMessage(messageID: Long): String?
|
fun getServerHashForMessage(messageID: Long): String?
|
||||||
fun getDatabaseAttachment(attachmentId: Long): DatabaseAttachment?
|
fun getDatabaseAttachment(attachmentId: Long): DatabaseAttachment?
|
||||||
|
@ -81,6 +81,7 @@ interface StorageProtocol {
|
|||||||
// Open Group Metadata
|
// Open Group Metadata
|
||||||
fun updateTitle(groupID: String, newValue: String)
|
fun updateTitle(groupID: String, newValue: String)
|
||||||
fun updateProfilePicture(groupID: String, newValue: ByteArray)
|
fun updateProfilePicture(groupID: String, newValue: ByteArray)
|
||||||
|
fun hasDownloadedProfilePicture(groupID: String): Boolean
|
||||||
fun setUserCount(room: String, server: String, newValue: Int)
|
fun setUserCount(room: String, server: String, newValue: Int)
|
||||||
|
|
||||||
// Last Message Server ID
|
// Last Message Server ID
|
||||||
|
@ -77,7 +77,11 @@ object FileServerApi {
|
|||||||
OnionRequestAPI.sendOnionRequest(requestBuilder.build(), server, serverPublicKey).map {
|
OnionRequestAPI.sendOnionRequest(requestBuilder.build(), server, serverPublicKey).map {
|
||||||
it.body ?: throw Error.ParsingFailed
|
it.body ?: throw Error.ParsingFailed
|
||||||
}.fail { e ->
|
}.fail { e ->
|
||||||
Log.e("Loki", "File server request failed.", e)
|
when (e) {
|
||||||
|
// No need for the stack trace for HTTP errors
|
||||||
|
is HTTP.HTTPRequestFailedException -> Log.e("Loki", "File server request failed due to error: ${e.message}")
|
||||||
|
else -> Log.e("Loki", "File server request failed", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Promise.ofFail(IllegalStateException("It's currently not allowed to send non onion routed requests."))
|
Promise.ofFail(IllegalStateException("It's currently not allowed to send non onion routed requests."))
|
||||||
|
@ -92,14 +92,25 @@ class BatchMessageReceiveJob(
|
|||||||
threadMap[threadID]!! += parsedParams
|
threadMap[threadID]!! += parsedParams
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Couldn't receive message.", e)
|
when (e) {
|
||||||
if (e is MessageReceiver.Error && !e.isRetryable) {
|
is MessageReceiver.Error.DuplicateMessage, MessageReceiver.Error.SelfSend -> {
|
||||||
Log.e(TAG, "Message failed permanently",e)
|
Log.i(TAG, "Couldn't receive message, failed with error: ${e.message}")
|
||||||
} else {
|
}
|
||||||
Log.e(TAG, "Message failed",e)
|
is MessageReceiver.Error -> {
|
||||||
|
if (!e.isRetryable) {
|
||||||
|
Log.e(TAG, "Couldn't receive message, failed permanently", e)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Log.e(TAG, "Couldn't receive message, failed", e)
|
||||||
failures += messageParameters
|
failures += messageParameters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else -> {
|
||||||
|
Log.e(TAG, "Couldn't receive message, failed", e)
|
||||||
|
failures += messageParameters
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterate over threads and persist them (persistence is the longest constant in the batch process operation)
|
// iterate over threads and persist them (persistence is the longest constant in the batch process operation)
|
||||||
|
@ -26,7 +26,7 @@ class JobQueue : JobDelegate {
|
|||||||
private val jobTimestampMap = ConcurrentHashMap<Long, AtomicInteger>()
|
private val jobTimestampMap = ConcurrentHashMap<Long, AtomicInteger>()
|
||||||
private val rxDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
private val rxDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
||||||
private val rxMediaDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()
|
private val rxMediaDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()
|
||||||
private val openGroupDispatcher = Executors.newCachedThreadPool().asCoroutineDispatcher()
|
private val openGroupDispatcher = Executors.newFixedThreadPool(8).asCoroutineDispatcher()
|
||||||
private val txDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
private val txDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
||||||
private val scope = CoroutineScope(Dispatchers.Default) + SupervisorJob()
|
private val scope = CoroutineScope(Dispatchers.Default) + SupervisorJob()
|
||||||
private val queue = Channel<Job>(UNLIMITED)
|
private val queue = Channel<Job>(UNLIMITED)
|
||||||
|
@ -11,6 +11,7 @@ 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.messaging.utilities.Data
|
import org.session.libsession.messaging.utilities.Data
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
|
import org.session.libsignal.utilities.HTTP
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
|
|
||||||
class MessageSendJob(val message: Message, val destination: Destination) : Job {
|
class MessageSendJob(val message: Message, val destination: Destination) : Job {
|
||||||
@ -67,14 +68,25 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
|
|||||||
val promise = MessageSender.send(this.message, this.destination).success {
|
val promise = MessageSender.send(this.message, this.destination).success {
|
||||||
this.handleSuccess()
|
this.handleSuccess()
|
||||||
}.fail { exception ->
|
}.fail { exception ->
|
||||||
Log.e(TAG, "Couldn't send message due to error: $exception.")
|
var logStacktrace = true
|
||||||
if (exception is MessageSender.Error) {
|
|
||||||
|
when (exception) {
|
||||||
|
// No need for the stack trace for HTTP errors
|
||||||
|
is HTTP.HTTPRequestFailedException -> {
|
||||||
|
logStacktrace = false
|
||||||
|
|
||||||
|
if (exception.statusCode == 429) { this.handlePermanentFailure(exception) }
|
||||||
|
else { this.handleFailure(exception) }
|
||||||
|
}
|
||||||
|
is MessageSender.Error -> {
|
||||||
if (!exception.isRetryable) { this.handlePermanentFailure(exception) }
|
if (!exception.isRetryable) { this.handlePermanentFailure(exception) }
|
||||||
|
else { this.handleFailure(exception) }
|
||||||
}
|
}
|
||||||
if (exception is OnionRequestAPI.HTTPRequestFailedAtDestinationException && exception.statusCode == 429) {
|
else -> this.handleFailure(exception)
|
||||||
this.handlePermanentFailure(exception)
|
|
||||||
}
|
}
|
||||||
this.handleFailure(exception)
|
|
||||||
|
if (logStacktrace) { Log.e(TAG, "Couldn't send message due to error", exception) }
|
||||||
|
else { Log.e(TAG, "Couldn't send message due to error: ${exception.message}") }
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
promise.get()
|
promise.get()
|
||||||
|
@ -23,15 +23,28 @@ class OpenGroupDeleteJob(private val messageServerIds: LongArray, private val th
|
|||||||
val dataProvider = MessagingModuleConfiguration.shared.messageDataProvider
|
val dataProvider = MessagingModuleConfiguration.shared.messageDataProvider
|
||||||
val numberToDelete = messageServerIds.size
|
val numberToDelete = messageServerIds.size
|
||||||
Log.d(TAG, "Deleting $numberToDelete messages")
|
Log.d(TAG, "Deleting $numberToDelete messages")
|
||||||
var numberDeleted = 0
|
|
||||||
messageServerIds.forEach { serverId ->
|
// FIXME: This entire process should probably run in a transaction (with the attachment deletion happening only if it succeeded)
|
||||||
val (messageId, isSms) = dataProvider.getMessageID(serverId, threadId) ?: return@forEach
|
try {
|
||||||
dataProvider.deleteMessage(messageId, isSms)
|
val messageIds = dataProvider.getMessageIDs(messageServerIds.toList(), threadId)
|
||||||
numberDeleted++
|
|
||||||
|
// Delete the SMS messages
|
||||||
|
if (messageIds.first.isNotEmpty()) {
|
||||||
|
dataProvider.deleteMessages(messageIds.first, threadId, true)
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Deleted $numberDeleted messages successfully")
|
|
||||||
|
// Delete the MMS messages
|
||||||
|
if (messageIds.second.isNotEmpty()) {
|
||||||
|
dataProvider.deleteMessages(messageIds.second, threadId, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Deleted ${messageIds.first.size + messageIds.second.size} messages successfully")
|
||||||
delegate?.handleJobSucceeded(this)
|
delegate?.handleJobSucceeded(this)
|
||||||
}
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
delegate?.handleJobFailed(this, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun serialize(): Data = Data.Builder()
|
override fun serialize(): Data = Data.Builder()
|
||||||
.putLongArray(MESSAGE_IDS, messageServerIds)
|
.putLongArray(MESSAGE_IDS, messageServerIds)
|
||||||
|
@ -383,7 +383,11 @@ object OpenGroupApi {
|
|||||||
}
|
}
|
||||||
return if (request.useOnionRouting) {
|
return if (request.useOnionRouting) {
|
||||||
OnionRequestAPI.sendOnionRequest(requestBuilder.build(), request.server, publicKey).fail { e ->
|
OnionRequestAPI.sendOnionRequest(requestBuilder.build(), request.server, publicKey).fail { e ->
|
||||||
Log.e("SOGS", "Failed onion request", e)
|
when (e) {
|
||||||
|
// No need for the stack trace for HTTP errors
|
||||||
|
is HTTP.HTTPRequestFailedException -> Log.e("SOGS", "Failed onion request: ${e.message}")
|
||||||
|
else -> Log.e("SOGS", "Failed onion request", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Promise.ofFail(IllegalStateException("It's currently not allowed to send non onion routed requests."))
|
Promise.ofFail(IllegalStateException("It's currently not allowed to send non onion routed requests."))
|
||||||
|
@ -117,6 +117,7 @@ class OpenGroupPoller(private val server: String, private val executorService: S
|
|||||||
) {
|
) {
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
val groupId = "$server.$roomToken"
|
val groupId = "$server.$roomToken"
|
||||||
|
val dbGroupId = GroupUtil.getEncodedOpenGroupID(groupId.toByteArray())
|
||||||
|
|
||||||
val existingOpenGroup = storage.getOpenGroup(roomToken, server)
|
val existingOpenGroup = storage.getOpenGroup(roomToken, server)
|
||||||
val publicKey = existingOpenGroup?.publicKey ?: return
|
val publicKey = existingOpenGroup?.publicKey ?: return
|
||||||
@ -157,8 +158,19 @@ class OpenGroupPoller(private val server: String, private val executorService: S
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start downloading the room image (if we don't have one or it's been updated)
|
if (
|
||||||
if (pollInfo.details?.imageId != null && pollInfo.details.imageId != existingOpenGroup.imageId) {
|
(
|
||||||
|
pollInfo.details != null &&
|
||||||
|
pollInfo.details.imageId != null && (
|
||||||
|
pollInfo.details.imageId != existingOpenGroup.imageId ||
|
||||||
|
!storage.hasDownloadedProfilePicture(dbGroupId)
|
||||||
|
)
|
||||||
|
) || (
|
||||||
|
pollInfo.details == null &&
|
||||||
|
existingOpenGroup.imageId != null &&
|
||||||
|
!storage.hasDownloadedProfilePicture(dbGroupId)
|
||||||
|
)
|
||||||
|
) {
|
||||||
JobQueue.shared.add(GroupAvatarDownloadJob(roomToken, server))
|
JobQueue.shared.add(GroupAvatarDownloadJob(roomToken, server))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,8 +78,8 @@ object OnionRequestAPI {
|
|||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
class HTTPRequestFailedBlindingRequiredException(statusCode: Int, json: Map<*, *>, destination: String): HTTPRequestFailedAtDestinationException(statusCode, json, destination)
|
class HTTPRequestFailedBlindingRequiredException(statusCode: Int, json: Map<*, *>, destination: String): HTTPRequestFailedAtDestinationException(statusCode, json, destination)
|
||||||
open class HTTPRequestFailedAtDestinationException(val statusCode: Int, val json: Map<*, *>, val destination: String)
|
open class HTTPRequestFailedAtDestinationException(statusCode: Int, json: Map<*, *>, val destination: String)
|
||||||
: Exception("HTTP request failed at destination ($destination) with status code $statusCode.")
|
: HTTP.HTTPRequestFailedException(statusCode, json, "HTTP request failed at destination ($destination) with status code $statusCode.")
|
||||||
class InsufficientSnodesException : Exception("Couldn't find enough snodes to build a path.")
|
class InsufficientSnodesException : Exception("Couldn't find enough snodes to build a path.")
|
||||||
|
|
||||||
private data class OnionBuildingResult(
|
private data class OnionBuildingResult(
|
||||||
|
@ -56,6 +56,10 @@ object SnodeAPI {
|
|||||||
* user's clock is incorrect.
|
* user's clock is incorrect.
|
||||||
*/
|
*/
|
||||||
internal var clockOffset = 0L
|
internal var clockOffset = 0L
|
||||||
|
|
||||||
|
val nowWithOffset
|
||||||
|
get() = System.currentTimeMillis() + clockOffset
|
||||||
|
|
||||||
internal var forkInfo by observable(database.getForkInfo()) { _, oldValue, newValue ->
|
internal var forkInfo by observable(database.getForkInfo()) { _, oldValue, newValue ->
|
||||||
if (newValue > oldValue) {
|
if (newValue > oldValue) {
|
||||||
Log.d("Loki", "Setting new fork info new: $newValue, old: $oldValue")
|
Log.d("Loki", "Setting new fork info new: $newValue, old: $oldValue")
|
||||||
|
@ -2,6 +2,7 @@ package org.session.libsession.utilities
|
|||||||
|
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import org.session.libsession.messaging.file_server.FileServerApi
|
import org.session.libsession.messaging.file_server.FileServerApi
|
||||||
|
import org.session.libsignal.utilities.HTTP
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import java.io.*
|
import java.io.*
|
||||||
|
|
||||||
@ -40,7 +41,11 @@ object DownloadUtilities {
|
|||||||
outputStream.write(it)
|
outputStream.write(it)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("Loki", "Couldn't download attachment.", e)
|
when (e) {
|
||||||
|
// No need for the stack trace for HTTP errors
|
||||||
|
is HTTP.HTTPRequestFailedException -> Log.e("Loki", "Couldn't download attachment due to error: ${e.message}")
|
||||||
|
else -> Log.e("Loki", "Couldn't download attachment", e)
|
||||||
|
}
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import javax.net.ssl.SSLContext
|
|||||||
import javax.net.ssl.X509TrustManager
|
import javax.net.ssl.X509TrustManager
|
||||||
|
|
||||||
object HTTP {
|
object HTTP {
|
||||||
|
var isConnectedToNetwork: (() -> Boolean) = { false }
|
||||||
|
|
||||||
private val seedNodeConnection by lazy {
|
private val seedNodeConnection by lazy {
|
||||||
OkHttpClient().newBuilder()
|
OkHttpClient().newBuilder()
|
||||||
@ -64,8 +65,12 @@ object HTTP {
|
|||||||
|
|
||||||
private const val timeout: Long = 120
|
private const val timeout: Long = 120
|
||||||
|
|
||||||
class HTTPRequestFailedException(val statusCode: Int, val json: Map<*, *>?)
|
open class HTTPRequestFailedException(
|
||||||
: kotlin.Exception("HTTP request failed with status code $statusCode.")
|
val statusCode: Int,
|
||||||
|
val json: Map<*, *>?,
|
||||||
|
message: String = "HTTP request failed with status code $statusCode"
|
||||||
|
) : kotlin.Exception(message)
|
||||||
|
class HTTPNoNetworkException : HTTPRequestFailedException(0, null, "No network connection")
|
||||||
|
|
||||||
enum class Verb(val rawValue: String) {
|
enum class Verb(val rawValue: String) {
|
||||||
GET("GET"), PUT("PUT"), POST("POST"), DELETE("DELETE")
|
GET("GET"), PUT("PUT"), POST("POST"), DELETE("DELETE")
|
||||||
@ -120,8 +125,11 @@ object HTTP {
|
|||||||
response = connection.newCall(request.build()).execute()
|
response = connection.newCall(request.build()).execute()
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.d("Loki", "${verb.rawValue} request to $url failed due to error: ${exception.localizedMessage}.")
|
Log.d("Loki", "${verb.rawValue} request to $url failed due to error: ${exception.localizedMessage}.")
|
||||||
|
|
||||||
|
if (!isConnectedToNetwork()) { throw HTTPNoNetworkException() }
|
||||||
|
|
||||||
// Override the actual error so that we can correctly catch failed requests in OnionRequestAPI
|
// Override the actual error so that we can correctly catch failed requests in OnionRequestAPI
|
||||||
throw HTTPRequestFailedException(0, null)
|
throw HTTPRequestFailedException(0, null, "HTTP request failed due to: ${exception.message}")
|
||||||
}
|
}
|
||||||
return when (val statusCode = response.code()) {
|
return when (val statusCode = response.code()) {
|
||||||
200 -> {
|
200 -> {
|
||||||
|
Loading…
Reference in New Issue
Block a user