diff --git a/app/build.gradle b/app/build.gradle index 2e52adddd59..23bbecd217d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -271,7 +271,7 @@ dependencies { if (project.hasProperty('huawei')) huaweiImplementation 'com.huawei.hms:push:6.7.0.300' implementation 'com.google.android.exoplayer:exoplayer-core:2.9.1' implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.1' - implementation 'org.conscrypt:conscrypt-android:2.0.0' + implementation 'org.conscrypt:conscrypt-android:2.5.2' implementation 'org.signal:aesgcmprovider:0.0.3' implementation 'org.webrtc:google-webrtc:1.0.32006' implementation "me.leolin:ShortcutBadger:1.1.16" diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt index 83c6904dec2..b320e72e265 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt @@ -22,6 +22,7 @@ import androidx.core.view.isVisible import network.loki.messenger.R import network.loki.messenger.databinding.ViewVisibleMessageContentBinding import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment @@ -289,7 +290,7 @@ class VisibleMessageContentView : ConstraintLayout { // replace URLSpans with ModalURLSpans body.getSpans(0, body.length).toList().forEach { urlSpan -> - val updatedUrl = urlSpan.url.let { HttpUrl.parse(it).toString() } + val updatedUrl = urlSpan.url.let { it.toHttpUrlOrNull().toString() } val replacementSpan = ModalURLSpan(updatedUrl) { url -> val activity = context as AppCompatActivity ModalUrlBottomSheet(url).show(activity.supportFragmentManager, "Open URL Dialog") diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java index 8dbef320172..dcd7778c9a2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java @@ -55,7 +55,7 @@ public class RecipientDatabase extends Database { private static final String SYSTEM_PHONE_LABEL = "system_phone_label"; private static final String SYSTEM_CONTACT_URI = "system_contact_uri"; private static final String SIGNAL_PROFILE_NAME = "signal_profile_name"; - private static final String SIGNAL_PROFILE_AVATAR = "signal_profile_avatar"; + private static final String SESSION_PROFILE_AVATAR = "signal_profile_avatar"; private static final String PROFILE_SHARING = "profile_sharing_approval"; private static final String CALL_RINGTONE = "call_ringtone"; private static final String CALL_VIBRATE = "call_vibrate"; @@ -69,7 +69,7 @@ public class RecipientDatabase extends Database { private static final String[] RECIPIENT_PROJECTION = new String[] { BLOCK, APPROVED, APPROVED_ME, NOTIFICATION, CALL_RINGTONE, VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, EXPIRE_MESSAGES, REGISTERED, PROFILE_KEY, SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_CONTACT_URI, - SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL, + SIGNAL_PROFILE_NAME, SESSION_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL, UNIDENTIFIED_ACCESS_MODE, FORCE_SMS_SELECTION, NOTIFY_TYPE, DISAPPEARING_STATE, WRAPPER_HASH, BLOCKS_COMMUNITY_MESSAGE_REQUESTS }; @@ -97,7 +97,7 @@ public class RecipientDatabase extends Database { SYSTEM_CONTACT_URI + " TEXT DEFAULT NULL, " + PROFILE_KEY + " TEXT DEFAULT NULL, " + SIGNAL_PROFILE_NAME + " TEXT DEFAULT NULL, " + - SIGNAL_PROFILE_AVATAR + " TEXT DEFAULT NULL, " + + SESSION_PROFILE_AVATAR + " TEXT DEFAULT NULL, " + PROFILE_SHARING + " INTEGER DEFAULT 0, " + CALL_RINGTONE + " TEXT DEFAULT NULL, " + CALL_VIBRATE + " INTEGER DEFAULT " + Recipient.VibrateState.DEFAULT.getId() + ", " + @@ -204,7 +204,7 @@ Optional getRecipientSettings(@NonNull Cursor cursor) { String systemPhoneLabel = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_PHONE_LABEL)); String systemContactUri = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_CONTACT_URI)); String signalProfileName = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_NAME)); - String signalProfileAvatar = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_AVATAR)); + String signalProfileAvatar = cursor.getString(cursor.getColumnIndexOrThrow(SESSION_PROFILE_AVATAR)); boolean profileSharing = cursor.getInt(cursor.getColumnIndexOrThrow(PROFILE_SHARING)) == 1; String notificationChannel = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION_CHANNEL)); int unidentifiedAccessMode = cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED_ACCESS_MODE)); @@ -361,7 +361,7 @@ public void setProfileKey(@NonNull Recipient recipient, @Nullable byte[] profile public void setProfileAvatar(@NonNull Recipient recipient, @Nullable String profileAvatar) { ContentValues contentValues = new ContentValues(1); - contentValues.put(SIGNAL_PROFILE_AVATAR, profileAvatar); + contentValues.put(SESSION_PROFILE_AVATAR, profileAvatar); updateOrInsert(recipient.getAddress(), contentValues); recipient.resolve().setProfileAvatar(profileAvatar); notifyRecipientListeners(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index dd0544b420b..29c637bc231 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.database import android.content.Context import android.net.Uri +import java.security.MessageDigest import network.loki.messenger.libsession_util.ConfigBase import network.loki.messenger.libsession_util.ConfigBase.Companion.PRIORITY_HIDDEN import network.loki.messenger.libsession_util.ConfigBase.Companion.PRIORITY_PINNED @@ -10,6 +11,7 @@ import network.loki.messenger.libsession_util.ConversationVolatileConfig import network.loki.messenger.libsession_util.UserGroupsConfig import network.loki.messenger.libsession_util.UserProfile import network.loki.messenger.libsession_util.util.BaseCommunityInfo +import network.loki.messenger.libsession_util.util.Contact as LibSessionContact import network.loki.messenger.libsession_util.util.Conversation import network.loki.messenger.libsession_util.util.ExpiryMode import network.loki.messenger.libsession_util.util.GroupInfo @@ -91,8 +93,6 @@ import org.thoughtcrime.securesms.groups.OpenGroupManager import org.thoughtcrime.securesms.mms.PartAuthority import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.SessionMetaProtocol -import java.security.MessageDigest -import network.loki.messenger.libsession_util.util.Contact as LibSessionContact private const val TAG = "Storage" @@ -471,7 +471,8 @@ open class Storage( val userPublicKey = getUserPublicKey() ?: return // would love to get rid of recipient and context from this val recipient = Recipient.from(context, fromSerialized(userPublicKey), false) - // update name + + // Update profile name val name = userProfile.getName() ?: return val userPic = userProfile.getPic() val profileManager = SSKEnvironment.shared.profileManager @@ -480,13 +481,14 @@ open class Storage( profileManager.setName(context, recipient, name) } - // update pfp + // Update profile picture if (userPic == UserPic.DEFAULT) { clearUserPic() } else if (userPic.key.isNotEmpty() && userPic.url.isNotEmpty() && TextSecurePreferences.getProfilePictureURL(context) != userPic.url) { setUserProfilePicture(userPic.url, userPic.key) } + if (userProfile.getNtsPriority() == PRIORITY_HIDDEN) { // delete nts thread if needed val ourThread = getThreadId(recipient) ?: return @@ -514,12 +516,13 @@ open class Storage( addLibSessionContacts(extracted, messageTimestamp) } - override fun clearUserPic() { - val userPublicKey = getUserPublicKey() ?: return + override fun clearUserPic() { + val userPublicKey = getUserPublicKey() ?: return Log.w(TAG, "No user public key when trying to clear user pic") val recipientDatabase = DatabaseComponent.get(context).recipientDatabase() - // would love to get rid of recipient and context from this + val recipient = Recipient.from(context, fromSerialized(userPublicKey), false) - // clear picture if userPic is null + + // Clear details related to the user's profile picture TextSecurePreferences.setProfileKey(context, null) ProfileKeyUtil.setEncodedProfileKey(context, null) recipientDatabase.setProfileAvatar(recipient, null) @@ -528,7 +531,6 @@ open class Storage( Recipient.removeCached(fromSerialized(userPublicKey)) configFactory.user?.setPic(UserPic.DEFAULT) - ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context) } private fun updateConvoVolatile(convos: ConversationVolatileConfig, messageTimestamp: Long) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupManager.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupManager.kt index 01e1c514ff8..8bb7a39d4af 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupManager.kt @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.groups import android.content.Context import androidx.annotation.WorkerThread import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.open_groups.GroupMemberRole import org.session.libsession.messaging.open_groups.OpenGroup @@ -143,9 +144,9 @@ object OpenGroupManager { @WorkerThread fun addOpenGroup(urlAsString: String, context: Context): OpenGroupApi.RoomInfo? { - val url = HttpUrl.parse(urlAsString) ?: return null + val url = urlAsString.toHttpUrlOrNull() ?: return null val server = OpenGroup.getServer(urlAsString) - val room = url.pathSegments().firstOrNull() ?: return null + val room = url.pathSegments.firstOrNull() ?: return null val publicKey = url.queryParameter("public_key") ?: return null return add(server.toString().removeSuffix("/"), room, publicKey, context).second // assume migrated from calling function diff --git a/app/src/main/java/org/thoughtcrime/securesms/logging/AndroidLogger.java b/app/src/main/java/org/thoughtcrime/securesms/logging/AndroidLogger.java index d62aac647c4..196e77e45af 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logging/AndroidLogger.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logging/AndroidLogger.java @@ -35,6 +35,5 @@ public void wtf(String tag, String message, Throwable t) { } @Override - public void blockUntilAllWritesFinished() { - } + public void blockUntilAllWritesFinished() { } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushRegistryV2.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushRegistryV2.kt index bf16333b15d..42ae798366b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushRegistryV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushRegistryV2.kt @@ -10,6 +10,7 @@ import kotlinx.serialization.json.decodeFromStream import nl.komponents.kovenant.Promise import nl.komponents.kovenant.functional.map import okhttp3.MediaType +import okhttp3.MediaType.Companion.toMediaType import okhttp3.Request import okhttp3.RequestBody import org.session.libsession.messaging.sending_receiving.notifications.Response @@ -99,7 +100,7 @@ class PushRegistryV2 @Inject constructor(private val pushReceiver: PushReceiver) private inline fun getResponseBody(path: String, requestParameters: String): Promise { val server = Server.LATEST val url = "${server.url}/$path" - val body = RequestBody.create(MediaType.get("application/json"), requestParameters) + val body = RequestBody.create("application/json".toMediaType(), requestParameters) val request = Request.Builder().url(url).post(body).build() return OnionRequestAPI.sendOnionRequest( diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt index b66df5d2552..e423b441701 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt @@ -22,13 +22,16 @@ import android.view.inputmethod.InputMethodManager import android.widget.Toast import androidx.core.view.isVisible import dagger.hilt.android.AndroidEntryPoint +import java.io.File +import java.security.SecureRandom +import javax.inject.Inject import network.loki.messenger.BuildConfig import network.loki.messenger.R import network.loki.messenger.databinding.ActivitySettingsBinding import network.loki.messenger.libsession_util.util.UserPic import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.all import nl.komponents.kovenant.ui.alwaysUi +import nl.komponents.kovenant.ui.failUi import nl.komponents.kovenant.ui.successUi import org.session.libsession.avatars.AvatarHelper import org.session.libsession.avatars.ProfileContactPhoto @@ -37,7 +40,7 @@ import org.session.libsession.snode.SnodeAPI import org.session.libsession.utilities.* import org.session.libsession.utilities.SSKEnvironment.ProfileManagerProtocol import org.session.libsession.utilities.recipients.Recipient -import org.session.libsignal.utilities.getProperty +import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.avatar.AvatarSelection import org.thoughtcrime.securesms.components.ProfilePictureView @@ -56,12 +59,10 @@ import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.disableClipping import org.thoughtcrime.securesms.util.push import org.thoughtcrime.securesms.util.show -import java.io.File -import java.security.SecureRandom -import javax.inject.Inject @AndroidEntryPoint class SettingsActivity : PassphraseRequiredActionBarActivity() { + private val TAG = "SettingsActivity" @Inject lateinit var configFactory: ConfigFactory @@ -233,41 +234,78 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { displayName: String? = null ) { binding.loader.isVisible = true - val promises = mutableListOf>() + if (displayName != null) { TextSecurePreferences.setProfileName(this, displayName) configFactory.user?.setName(displayName) } + + // Bail if we're not updating the profile picture in any way + if (!isUpdatingProfilePicture) return + val encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this) - if (isUpdatingProfilePicture) { - if (profilePicture != null) { - promises.add(ProfilePictureUtilities.upload(profilePicture, encodedProfileKey, this)) - } else { + + val uploadProfilePicturePromise: Promise<*, Exception> + var removingProfilePic = false + + // Adding a new profile picture? + if (profilePicture != null) { + uploadProfilePicturePromise = ProfilePictureUtilities.upload(profilePicture, encodedProfileKey, this) + } else { + // If not then we must be removing the existing one. + // Note: To get a promise that will resolve / sync correctly we overwrite the existing profile picture with + // a 0 byte image. + removingProfilePic = true + val emptyByteArray = ByteArray(0) + uploadProfilePicturePromise = ProfilePictureUtilities.upload(emptyByteArray, encodedProfileKey, this) + } + + // If the upload picture promise succeeded then we hit this successUi block + uploadProfilePicturePromise.successUi { + + // If we successfully removed the profile picture on the network then we can clear the + // local data - otherwise it's weird to fail the online section but it _looks_ like it + // worked because we cleared the local image (also it denies them the chance to retry + // removal if we do it locally, and may result in them having a visible profile picture + // everywhere EXCEPT on their own device!). + if (removingProfilePic) { MessagingModuleConfiguration.shared.storage.clearUserPic() } - } - val compoundPromise = all(promises) - compoundPromise.successUi { // Do this on the UI thread so that it happens before the alwaysUi clause below + val userConfig = configFactory.user - if (isUpdatingProfilePicture) { - AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture) - TextSecurePreferences.setProfileAvatarId(this, profilePicture?.let { SecureRandom().nextInt() } ?: 0 ) - ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey) - // new config - val url = TextSecurePreferences.getProfilePictureURL(this) - val profileKey = ProfileKeyUtil.getProfileKey(this) - if (profilePicture == null) { - userConfig?.setPic(UserPic.DEFAULT) - } else if (!url.isNullOrEmpty() && profileKey.isNotEmpty()) { - userConfig?.setPic(UserPic(url, profileKey)) - } + AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture) + TextSecurePreferences.setProfileAvatarId(this, profilePicture?.let { SecureRandom().nextInt() } ?: 0 ) + ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey) + + // new config + val url = TextSecurePreferences.getProfilePictureURL(this) + val profileKey = ProfileKeyUtil.getProfileKey(this) + + // If we have a URL and a profile key then set the user's profile picture + if (!url.isNullOrEmpty() && profileKey.isNotEmpty()) { + userConfig?.setPic(UserPic(url, profileKey)) } + if (userConfig != null && userConfig.needsDump()) { configFactory.persist(userConfig, SnodeAPI.nowWithOffset) } + ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@SettingsActivity) } - compoundPromise.alwaysUi { + + // Or if the promise failed to upload the new profile picture then we hit this failUi block + uploadProfilePicturePromise.failUi { + if (removingProfilePic) { + Log.e(TAG, "Failed to remove profile picture") + Toast.makeText(this@SettingsActivity, R.string.profileDisplayPictureRemoveError, Toast.LENGTH_LONG).show() + } else { + Log.e(TAG, "Failed to upload profile picture") + Toast.makeText(this@SettingsActivity, R.string.profileErrorUpdate, Toast.LENGTH_LONG).show() + } + } + + // Finally, regardless of whether the promise succeeded or failed, we always hit this `alwaysUi` block + uploadProfilePicturePromise.alwaysUi { if (displayName != null) { binding.btnGroupNameDisplay.text = displayName } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationMessageUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationMessageUtilities.kt index e0f04924456..a4676143766 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationMessageUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationMessageUtilities.kt @@ -24,6 +24,7 @@ import org.session.libsession.utilities.WindowDebouncer import org.session.libsignal.crypto.ecc.DjbECPublicKey import org.session.libsignal.utilities.Hex import org.session.libsignal.utilities.IdPrefix +import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.toHexString import org.thoughtcrime.securesms.database.GroupDatabase import org.thoughtcrime.securesms.database.ThreadDatabase @@ -31,10 +32,12 @@ import org.thoughtcrime.securesms.dependencies.DatabaseComponent import java.util.Timer object ConfigurationMessageUtilities { + private const val TAG = "ConfigMessageUtils" private val debouncer = WindowDebouncer(3000, Timer()) private fun scheduleConfigSync(userPublicKey: String) { + debouncer.publish { // don't schedule job if we already have one val storage = MessagingModuleConfiguration.shared.storage @@ -44,23 +47,20 @@ object ConfigurationMessageUtilities { (currentStorageJob as ConfigurationSyncJob).shouldRunAgain.set(true) return@publish } - val newConfigSync = ConfigurationSyncJob(ourDestination) - JobQueue.shared.add(newConfigSync) + val newConfigSyncJob = ConfigurationSyncJob(ourDestination) + JobQueue.shared.add(newConfigSyncJob) } } @JvmStatic fun syncConfigurationIfNeeded(context: Context) { - // add if check here to schedule new config job process and return early - val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return + val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return Log.w(TAG, "User Public Key is null") scheduleConfigSync(userPublicKey) } fun forceSyncConfigurationNowIfNeeded(context: Context): Promise { - // add if check here to schedule new config job process and return early val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return Promise.ofFail(NullPointerException("User Public Key is null")) - // schedule job if none exist - // don't schedule job if we already have one + // Schedule a new job if one doesn't already exist (only) scheduleConfigSync(userPublicKey) return Promise.ofSuccess(Unit) } diff --git a/gradle.properties b/gradle.properties index 1d7bc62d40d..f41dc761bc5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,7 +21,7 @@ googleServicesVersion=4.3.12 kotlinVersion=1.8.21 android.useAndroidX=true appcompatVersion=1.6.1 -coreVersion=1.8.0 +coreVersion=1.13.1 coroutinesVersion=1.6.4 curve25519Version=0.6.0 daggerVersion=2.46.1 @@ -33,7 +33,7 @@ kovenantVersion=3.3.0 lifecycleVersion=2.5.1 materialVersion=1.8.0 mockitoKotlinVersion=4.1.0 -okhttpVersion=3.12.1 +okhttpVersion=4.12.0 pagingVersion=3.0.0 preferenceVersion=1.2.0 protobufVersion=2.5.0 diff --git a/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt b/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt index 0e8768d530d..5bffed57ee8 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt @@ -3,8 +3,11 @@ package org.session.libsession.messaging.file_server import nl.komponents.kovenant.Promise import nl.komponents.kovenant.functional.map import okhttp3.Headers +import okhttp3.Headers.Companion.toHeaders import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.MediaType +import okhttp3.MediaType.Companion.toMediaType import okhttp3.RequestBody import org.session.libsession.snode.OnionRequestAPI import org.session.libsignal.utilities.HTTP @@ -37,18 +40,18 @@ object FileServerApi { ) private fun createBody(body: ByteArray?, parameters: Any?): RequestBody? { - if (body != null) return RequestBody.create(MediaType.get("application/octet-stream"), body) + if (body != null) return RequestBody.create("application/octet-stream".toMediaType(), body) if (parameters == null) return null val parametersAsJSON = JsonUtil.toJson(parameters) - return RequestBody.create(MediaType.get("application/json"), parametersAsJSON) + return RequestBody.create("application/json".toMediaType(), parametersAsJSON) } private fun send(request: Request): Promise { - val url = HttpUrl.parse(server) ?: return Promise.ofFail(Error.InvalidURL) + val url = server.toHttpUrlOrNull() ?: return Promise.ofFail(Error.InvalidURL) val urlBuilder = HttpUrl.Builder() - .scheme(url.scheme()) - .host(url.host()) - .port(url.port()) + .scheme(url.scheme) + .host(url.host) + .port(url.port) .addPathSegments(request.endpoint) if (request.verb == HTTP.Verb.GET) { for ((key, value) in request.queryParameters) { @@ -57,7 +60,7 @@ object FileServerApi { } val requestBuilder = okhttp3.Request.Builder() .url(urlBuilder.build()) - .headers(Headers.of(request.headers)) + .headers(request.headers.toHeaders()) when (request.verb) { HTTP.Verb.GET -> requestBuilder.get() HTTP.Verb.PUT -> requestBuilder.put(createBody(request.body, request.parameters)!!) diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt index ffa05bf1e6f..0da398dff2e 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt @@ -1,6 +1,7 @@ package org.session.libsession.messaging.jobs import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.session.libsession.database.MessageDataProvider import org.session.libsession.database.StorageProtocol import org.session.libsession.messaging.MessagingModuleConfiguration @@ -141,8 +142,8 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long) DownloadUtilities.downloadFile(tempFile, attachment.url) } else { Log.d("AttachmentDownloadJob", "downloading open group attachment") - val url = HttpUrl.parse(attachment.url)!! - val fileID = url.pathSegments().last() + val url = attachment.url.toHttpUrlOrNull()!! + val fileID = url.pathSegments.last() OpenGroupApi.download(fileID, openGroup.room, openGroup.server).get().let { tempFile.writeBytes(it) } diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt index 19b6555b50c..1e6d4836037 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt @@ -176,7 +176,7 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess val kryo = Kryo() kryo.isRegistrationRequired = false val serializedMessage = ByteArray(4096) - val output = Output(serializedMessage, Job.MAX_BUFFER_SIZE) + val output = Output(serializedMessage, Job.MAX_BUFFER_SIZE_BYTES) kryo.writeClassAndObject(output, message) output.close() return Data.Builder() diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/BackgroundGroupAddJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/BackgroundGroupAddJob.kt index f284f2539d8..5f7bb34ce47 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/BackgroundGroupAddJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/BackgroundGroupAddJob.kt @@ -1,6 +1,7 @@ package org.session.libsession.messaging.jobs import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.open_groups.OpenGroup import org.session.libsession.messaging.utilities.Data @@ -21,9 +22,9 @@ class BackgroundGroupAddJob(val joinUrl: String): Job { override val maxFailureCount: Int = 1 val openGroupId: String? get() { - val url = HttpUrl.parse(joinUrl) ?: return null + val url = joinUrl.toHttpUrlOrNull() ?: return null val server = OpenGroup.getServer(joinUrl)?.toString()?.removeSuffix("/") ?: return null - val room = url.pathSegments().firstOrNull() ?: return null + val room = url.pathSegments.firstOrNull() ?: return null return "$server.$room" } diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt index d7dfdf768ed..4a3299d197c 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt @@ -1,5 +1,6 @@ package org.session.libsession.messaging.jobs +import java.util.concurrent.atomic.AtomicBoolean import network.loki.messenger.libsession_util.ConfigBase.Companion.protoKindFor import nl.komponents.kovenant.functional.bind import org.session.libsession.messaging.MessagingModuleConfiguration @@ -10,7 +11,6 @@ import org.session.libsession.messaging.utilities.Data import org.session.libsession.snode.RawResponse import org.session.libsession.snode.SnodeAPI import org.session.libsignal.utilities.Log -import java.util.concurrent.atomic.AtomicBoolean // only contact (self) and closed group destinations will be supported data class ConfigurationSyncJob(val destination: Destination): Job { @@ -180,7 +180,6 @@ data class ConfigurationSyncJob(val destination: Destination): Job { // type mappings const val CONTACT_TYPE = 1 const val GROUP_TYPE = 2 - } class Factory: Job.Factory { diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/Job.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/Job.kt index 7f3bf9b1733..8e9bcf839ca 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/Job.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/Job.kt @@ -14,7 +14,7 @@ interface Job { // Keys used for database storage private val ID_KEY = "id" private val FAILURE_COUNT_KEY = "failure_count" - internal const val MAX_BUFFER_SIZE = 1_000_000 // bytes + internal const val MAX_BUFFER_SIZE_BYTES = 1_000_000 // ~1MB } suspend fun execute(dispatcherName: String) diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt index 2a152d0a015..52d56184cc3 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt @@ -4,7 +4,7 @@ import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Output import org.session.libsession.messaging.MessagingModuleConfiguration -import org.session.libsession.messaging.jobs.Job.Companion.MAX_BUFFER_SIZE +import org.session.libsession.messaging.jobs.Job.Companion.MAX_BUFFER_SIZE_BYTES import org.session.libsession.messaging.messages.Destination import org.session.libsession.messaging.messages.Message import org.session.libsession.messaging.messages.visible.VisibleMessage @@ -118,12 +118,12 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job { val kryo = Kryo() kryo.isRegistrationRequired = false // Message - val messageOutput = Output(ByteArray(4096), MAX_BUFFER_SIZE) + val messageOutput = Output(ByteArray(4096), MAX_BUFFER_SIZE_BYTES) kryo.writeClassAndObject(messageOutput, message) messageOutput.close() val serializedMessage = messageOutput.toBytes() // Destination - val destinationOutput = Output(ByteArray(4096), MAX_BUFFER_SIZE) + val destinationOutput = Output(ByteArray(4096), MAX_BUFFER_SIZE_BYTES) kryo.writeClassAndObject(destinationOutput, destination) destinationOutput.close() val serializedDestination = destinationOutput.toBytes() diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/NotifyPNServerJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/NotifyPNServerJob.kt index 79c30f67e87..26a0cfb6e80 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/NotifyPNServerJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/NotifyPNServerJob.kt @@ -3,10 +3,10 @@ package org.session.libsession.messaging.jobs import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Output -import okhttp3.MediaType +import okhttp3.MediaType.Companion.toMediaType import okhttp3.Request import okhttp3.RequestBody -import org.session.libsession.messaging.jobs.Job.Companion.MAX_BUFFER_SIZE +import org.session.libsession.messaging.jobs.Job.Companion.MAX_BUFFER_SIZE_BYTES import org.session.libsession.messaging.sending_receiving.notifications.Server import org.session.libsession.messaging.utilities.Data import org.session.libsession.snode.OnionRequestAPI @@ -33,7 +33,7 @@ class NotifyPNServerJob(val message: SnodeMessage) : Job { val server = Server.LEGACY val parameters = mapOf( "data" to message.data, "send_to" to message.recipient ) val url = "${server.url}/notify" - val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) + val body = RequestBody.create("application/json".toMediaType(), JsonUtil.toJson(parameters)) val request = Request.Builder().url(url).post(body).build() retryIfNeeded(4) { OnionRequestAPI.sendOnionRequest( @@ -67,7 +67,7 @@ class NotifyPNServerJob(val message: SnodeMessage) : Job { val kryo = Kryo() kryo.isRegistrationRequired = false val serializedMessage = ByteArray(4096) - val output = Output(serializedMessage, MAX_BUFFER_SIZE) + val output = Output(serializedMessage, MAX_BUFFER_SIZE_BYTES) kryo.writeObject(output, message) output.close() return Data.Builder() diff --git a/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroup.kt b/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroup.kt index 80a9a1e5013..7743cd81768 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroup.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroup.kt @@ -1,6 +1,7 @@ package org.session.libsession.messaging.open_groups import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.session.libsignal.utilities.JsonUtil import org.session.libsignal.utilities.Log import java.util.Locale @@ -47,11 +48,11 @@ data class OpenGroup( } fun getServer(urlAsString: String): HttpUrl? { - val url = HttpUrl.parse(urlAsString) ?: return null - val builder = HttpUrl.Builder().scheme(url.scheme()).host(url.host()) - if (url.port() != 80 || url.port() != 443) { + val url = urlAsString.toHttpUrlOrNull() ?: return null + val builder = HttpUrl.Builder().scheme(url.scheme).host(url.host) + if (url.port != 80 || url.port != 443) { // Non-standard port; add to server - builder.port(url.port()) + builder.port(url.port) } return builder.build() } diff --git a/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroupApi.kt b/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroupApi.kt index 6e73c16f5e7..50eb7be7b06 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroupApi.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroupApi.kt @@ -14,8 +14,11 @@ import kotlinx.coroutines.flow.MutableSharedFlow import nl.komponents.kovenant.Promise import nl.komponents.kovenant.functional.map import okhttp3.Headers +import okhttp3.Headers.Companion.toHeaders import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.MediaType +import okhttp3.MediaType.Companion.toMediaType import okhttp3.RequestBody import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller.Companion.maxInactivityPeriod @@ -282,10 +285,10 @@ object OpenGroupApi { ) private fun createBody(body: ByteArray?, parameters: Any?): RequestBody? { - if (body != null) return RequestBody.create(MediaType.get("application/octet-stream"), body) + if (body != null) return RequestBody.create("application/octet-stream".toMediaType(), body) if (parameters == null) return null val parametersAsJSON = JsonUtil.toJson(parameters) - return RequestBody.create(MediaType.get("application/json"), parametersAsJSON) + return RequestBody.create("application/json".toMediaType(), parametersAsJSON) } private fun getResponseBody(request: Request): Promise { @@ -301,7 +304,7 @@ object OpenGroupApi { } private fun send(request: Request): Promise { - HttpUrl.parse(request.server) ?: return Promise.ofFail(Error.InvalidURL) + request.server.toHttpUrlOrNull() ?: return Promise.ofFail(Error.InvalidURL) val urlBuilder = StringBuilder("${request.server}/${request.endpoint.value}") if (request.verb == GET && request.queryParameters.isNotEmpty()) { urlBuilder.append("?") @@ -387,7 +390,7 @@ object OpenGroupApi { val requestBuilder = okhttp3.Request.Builder() .url(urlRequest) - .headers(Headers.of(headers)) + .headers(headers.toHeaders()) when (request.verb) { GET -> requestBuilder.get() PUT -> requestBuilder.put(createBody(request.body, request.parameters)!!) diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/notifications/PushRegistryV1.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/notifications/PushRegistryV1.kt index 1599dd93d52..230fb2dc9c2 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/notifications/PushRegistryV1.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/notifications/PushRegistryV1.kt @@ -3,6 +3,7 @@ package org.session.libsession.messaging.sending_receiving.notifications import android.annotation.SuppressLint import nl.komponents.kovenant.Promise import okhttp3.MediaType +import okhttp3.MediaType.Companion.toMediaType import okhttp3.Request import okhttp3.RequestBody import org.session.libsession.messaging.MessagingModuleConfiguration @@ -58,7 +59,7 @@ object PushRegistryV1 { val url = "${server.url}/register_legacy_groups_only" val body = RequestBody.create( - MediaType.get("application/json"), + "application/json".toMediaType(), JsonUtil.toJson(parameters) ) val request = Request.Builder().url(url).post(body).build() @@ -83,7 +84,7 @@ object PushRegistryV1 { return retryIfNeeded(maxRetryCount) { val parameters = mapOf("token" to token) val url = "${server.url}/unregister" - val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) + val body = RequestBody.create("application/json".toMediaType(), JsonUtil.toJson(parameters)) val request = Request.Builder().url(url).post(body).build() sendOnionRequest(request) success { @@ -120,7 +121,7 @@ object PushRegistryV1 { ): Promise<*, Exception> { val parameters = mapOf("closedGroupPublicKey" to closedGroupPublicKey, "pubKey" to publicKey) val url = "${server.url}/$operation" - val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) + val body = RequestBody.create("application/json".toMediaType(), JsonUtil.toJson(parameters)) val request = Request.Builder().url(url).post(body).build() return retryIfNeeded(maxRetryCount) { diff --git a/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt b/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt index cf43c7b14ae..8e55f286bc2 100644 --- a/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt @@ -467,9 +467,9 @@ object OnionRequestAPI { x25519PublicKey: String, version: Version = Version.V4 ): Promise { - val url = request.url() + val url = request.url val payload = generatePayload(request, server, version) - val destination = Destination.Server(url.host(), version.value, x25519PublicKey, url.scheme(), url.port()) + val destination = Destination.Server(url.host, version.value, x25519PublicKey, url.scheme, url.port) return sendOnionRequest(destination, payload, version).recover { exception -> Log.d("Loki", "Couldn't reach server: $url due to error: $exception.") throw exception @@ -478,7 +478,7 @@ object OnionRequestAPI { private fun generatePayload(request: Request, server: String, version: Version): ByteArray { val headers = request.getHeadersForOnionRequest().toMutableMap() - val url = request.url() + val url = request.url val urlAsString = url.toString() val body = request.getBodyForOnionRequest() ?: "null" val endpoint = when { @@ -486,19 +486,19 @@ object OnionRequestAPI { else -> "" } return if (version == Version.V4) { - if (request.body() != null && + if (request.body != null && headers.keys.find { it.equals("Content-Type", true) } == null) { headers["Content-Type"] = "application/json" } val requestPayload = mapOf( "endpoint" to endpoint, - "method" to request.method(), + "method" to request.method, "headers" to headers ) val requestData = JsonUtil.toJson(requestPayload).toByteArray() val prefixData = "l${requestData.size}:".toByteArray(Charsets.US_ASCII) val suffixData = "e".toByteArray(Charsets.US_ASCII) - if (request.body() != null) { + if (request.body != null) { val bodyData = if (body is ByteArray) body else body.toString().toByteArray() val bodyLengthData = "${bodyData.size}:".toByteArray(Charsets.US_ASCII) prefixData + requestData + bodyLengthData + bodyData + suffixData @@ -509,7 +509,7 @@ object OnionRequestAPI { val payload = mapOf( "body" to body, "endpoint" to endpoint.removePrefix("/"), - "method" to request.method(), + "method" to request.method, "headers" to headers ) JsonUtil.toJson(payload).toByteArray() diff --git a/libsession/src/main/java/org/session/libsession/snode/utilities/OKHTTPUtilities.kt b/libsession/src/main/java/org/session/libsession/snode/utilities/OKHTTPUtilities.kt index 4c08c4791e6..981664f2d33 100644 --- a/libsession/src/main/java/org/session/libsession/snode/utilities/OKHTTPUtilities.kt +++ b/libsession/src/main/java/org/session/libsession/snode/utilities/OKHTTPUtilities.kt @@ -9,13 +9,13 @@ import java.util.Locale internal fun Request.getHeadersForOnionRequest(): Map { val result = mutableMapOf() - val contentType = body()?.contentType() + val contentType = body?.contentType() if (contentType != null) { result["content-type"] = contentType.toString() } - val headers = headers() + val headers = headers for (name in headers.names()) { - val value = headers.get(name) + val value = headers[name] if (value != null) { if (value.toLowerCase(Locale.US) == "true" || value.toLowerCase(Locale.US) == "false") { result[name] = value.toBoolean() @@ -33,7 +33,7 @@ internal fun Request.getBodyForOnionRequest(): Any? { try { val copyOfThis = newBuilder().build() val buffer = Buffer() - val body = copyOfThis.body() ?: return null + val body = copyOfThis.body ?: return null body.writeTo(buffer) val bodyAsData = buffer.readByteArray() if (body is MultipartBody) { diff --git a/libsession/src/main/java/org/session/libsession/utilities/DownloadUtilities.kt b/libsession/src/main/java/org/session/libsession/utilities/DownloadUtilities.kt index 27b6b244bac..a7b19ed6e55 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/DownloadUtilities.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/DownloadUtilities.kt @@ -1,6 +1,7 @@ package org.session.libsession.utilities import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.session.libsession.messaging.file_server.FileServerApi import org.session.libsignal.utilities.HTTP import org.session.libsignal.utilities.Log @@ -36,8 +37,8 @@ object DownloadUtilities { */ @JvmStatic fun downloadFile(outputStream: OutputStream, urlAsString: String) { - val url = HttpUrl.parse(urlAsString)!! - val fileID = url.pathSegments().last() + val url = urlAsString.toHttpUrlOrNull()!! + val fileID = url.pathSegments.last() try { FileServerApi.download(fileID).get().let { outputStream.write(it) diff --git a/libsession/src/main/java/org/session/libsession/utilities/OpenGroupUrlParser.kt b/libsession/src/main/java/org/session/libsession/utilities/OpenGroupUrlParser.kt index d39128d5dc5..cac7faf096d 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/OpenGroupUrlParser.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/OpenGroupUrlParser.kt @@ -1,6 +1,7 @@ package org.session.libsession.utilities import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.session.libsession.messaging.open_groups.migrateLegacyServerUrl object OpenGroupUrlParser { @@ -19,14 +20,14 @@ object OpenGroupUrlParser { // URL has to start with 'http://' val urlWithPrefix = if (!string.startsWith("http")) "http://$string" else string // If the URL is malformed, throw an exception - val url = HttpUrl.parse(urlWithPrefix) ?: throw Error.MalformedURL + val url = urlWithPrefix.toHttpUrlOrNull() ?: throw Error.MalformedURL // Parse components - val server = HttpUrl.Builder().scheme(url.scheme()).host(url.host()).port(url.port()).build().toString().removeSuffix(suffix).migrateLegacyServerUrl() - val room = url.pathSegments().firstOrNull { !it.isNullOrEmpty() } ?: throw Error.NoRoom + val server = HttpUrl.Builder().scheme(url.scheme).host(url.host).port(url.port).build().toString().removeSuffix(suffix).migrateLegacyServerUrl() + val room = url.pathSegments.firstOrNull { !it.isNullOrEmpty() } ?: throw Error.NoRoom val publicKey = url.queryParameter(queryPrefix) ?: throw Error.NoPublicKey if (publicKey.length != 64) throw Error.InvalidPublicKey // Return - return V2OpenGroupInfo(server,room,publicKey) + return V2OpenGroupInfo(server, room, publicKey) } fun trimQueryParameter(string: String): String { diff --git a/libsession/src/main/java/org/session/libsession/utilities/WindowDebouncer.kt b/libsession/src/main/java/org/session/libsession/utilities/WindowDebouncer.kt index c3850e64e6a..bec5c938379 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/WindowDebouncer.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/WindowDebouncer.kt @@ -9,7 +9,7 @@ import java.util.concurrent.atomic.AtomicReference * Not really a 'debouncer' but named to be similar to the current Debouncer * designed to queue tasks on a window (if not already queued) like a timer */ -class WindowDebouncer(private val window: Long, private val timer: Timer) { +class WindowDebouncer(private val timeWindowMilliseconds: Long, private val timer: Timer) { private val atomicRef: AtomicReference = AtomicReference(null) private val hasStarted = AtomicBoolean(false) @@ -23,7 +23,7 @@ class WindowDebouncer(private val window: Long, private val timer: Timer) { fun publish(runnable: Runnable) { if (hasStarted.compareAndSet(false, true)) { - timer.scheduleAtFixedRate(recursiveRunnable, 0, window) + timer.scheduleAtFixedRate(recursiveRunnable, 0, timeWindowMilliseconds) } atomicRef.compareAndSet(null, runnable) } diff --git a/libsession/src/main/res/values/strings.xml b/libsession/src/main/res/values/strings.xml index a6a9e9043b9..53cc1bacca4 100644 --- a/libsession/src/main/res/values/strings.xml +++ b/libsession/src/main/res/values/strings.xml @@ -80,4 +80,7 @@ Clear Device Clear device only Clear device and network + + Failed to remove display picture. + Failed to update profile. diff --git a/libsignal/src/main/java/org/session/libsignal/utilities/HTTP.kt b/libsignal/src/main/java/org/session/libsignal/utilities/HTTP.kt index 5eac7cecd4d..0c1b2f83177 100644 --- a/libsignal/src/main/java/org/session/libsignal/utilities/HTTP.kt +++ b/libsignal/src/main/java/org/session/libsignal/utilities/HTTP.kt @@ -1,6 +1,7 @@ package org.session.libsignal.utilities -import okhttp3.MediaType +import android.util.Log +import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody @@ -11,10 +12,12 @@ import java.util.concurrent.TimeUnit import javax.net.ssl.SSLContext import javax.net.ssl.X509TrustManager + object HTTP { var isConnectedToNetwork: (() -> Boolean) = { false } private val seedNodeConnection by lazy { + OkHttpClient().newBuilder() .callTimeout(timeout, TimeUnit.SECONDS) .connectTimeout(timeout, TimeUnit.SECONDS) @@ -106,7 +109,7 @@ object HTTP { Verb.GET -> request.get() Verb.PUT, Verb.POST -> { if (body == null) { throw Exception("Invalid request body.") } - val contentType = MediaType.get("application/json; charset=utf-8") + val contentType = "application/json; charset=utf-8".toMediaType() @Suppress("NAME_SHADOWING") val body = RequestBody.create(contentType, body) if (verb == Verb.PUT) request.put(body) else request.post(body) } @@ -114,7 +117,7 @@ object HTTP { } lateinit var response: Response try { - val connection = if (timeout != HTTP.timeout) { // Custom timeout + val connection: OkHttpClient = if (timeout != HTTP.timeout) { // Custom timeout if (useSeedNodeConnection) { throw IllegalStateException("Setting a custom timeout is only allowed for requests to snodes.") } @@ -122,6 +125,7 @@ object HTTP { } else { if (useSeedNodeConnection) seedNodeConnection else defaultConnection } + response = connection.newCall(request.build()).execute() } catch (exception: Exception) { Log.d("Loki", "${verb.rawValue} request to $url failed due to error: ${exception.localizedMessage}.") @@ -131,9 +135,9 @@ object HTTP { // Override the actual error so that we can correctly catch failed requests in OnionRequestAPI throw HTTPRequestFailedException(0, null, "HTTP request failed due to: ${exception.message}") } - return when (val statusCode = response.code()) { + return when (val statusCode = response.code) { 200 -> { - response.body()?.bytes() ?: throw Exception("An error occurred.") + response.body!!.bytes() } else -> { Log.d("Loki", "${verb.rawValue} request to $url failed with status code: $statusCode.")