diff --git a/libsession/src/main/java/org/session/libsession/messaging/Configuration.kt b/libsession/src/main/java/org/session/libsession/messaging/Configuration.kt
deleted file mode 100644
index c544a972c7..0000000000
--- a/libsession/src/main/java/org/session/libsession/messaging/Configuration.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.session.libsession.messaging
-
-import org.session.libsession.database.MessageDataProvider
-import org.session.libsignal.libsignal.loki.SessionResetProtocol
-import org.session.libsignal.libsignal.state.*
-import org.session.libsignal.metadata.certificate.CertificateValidator
-import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol
-
-class Configuration(
- val storage: StorageProtocol,
- val signalStorage: SignalProtocolStore,
- val sskDatabase: SharedSenderKeysDatabaseProtocol,
- val messageDataProvider: MessageDataProvider,
- val sessionResetImp: SessionResetProtocol,
- val certificateValidator: CertificateValidator)
-{
- companion object {
- lateinit var shared: Configuration
-
- fun configure(storage: StorageProtocol,
- signalStorage: SignalProtocolStore,
- sskDatabase: SharedSenderKeysDatabaseProtocol,
- messageDataProvider: MessageDataProvider,
- sessionResetImp: SessionResetProtocol,
- certificateValidator: CertificateValidator
- ) {
- if (Companion::shared.isInitialized) { return }
- shared = Configuration(storage, signalStorage, sskDatabase, messageDataProvider, sessionResetImp, certificateValidator)
- }
- }
-}
\ No newline at end of file
diff --git a/libsession/src/main/java/org/session/libsession/messaging/threads/Address.kt b/libsession/src/main/java/org/session/libsession/messaging/threads/Address.kt
new file mode 100644
index 0000000000..a4e01330a3
--- /dev/null
+++ b/libsession/src/main/java/org/session/libsession/messaging/threads/Address.kt
@@ -0,0 +1,181 @@
+package org.session.libsession.messaging.threads
+
+import android.content.Context
+import android.os.Parcel
+import android.os.Parcelable
+import android.util.Pair
+import androidx.annotation.VisibleForTesting
+import org.session.libsession.utilities.DelimiterUtil.escape
+import org.session.libsession.utilities.DelimiterUtil.split
+import org.session.libsession.utilities.DelimiterUtil.unescape
+import org.session.libsession.utilities.GroupUtil
+import org.session.libsession.utilities.NumberUtil.isValidEmail
+import org.session.libsignal.libsignal.util.guava.Optional
+import org.session.libsignal.service.internal.util.Util
+import java.lang.AssertionError
+import java.util.*
+import java.util.concurrent.atomic.AtomicReference
+import java.util.regex.Matcher
+import java.util.regex.Pattern
+
+class Address private constructor(address: String) : Parcelable, Comparable
{
+ private val address: String = address.toLowerCase()
+
+ constructor(`in`: Parcel) : this(`in`.readString()!!) {}
+
+ val isGroup: Boolean
+ get() = GroupUtil.isEncodedGroup(address)
+ val isClosedGroup: Boolean
+ get() = GroupUtil.isClosedGroup(address)
+ val isOpenGroup: Boolean
+ get() = GroupUtil.isOpenGroup(address)
+ val isMmsGroup: Boolean
+ get() = GroupUtil.isMmsGroup(address)
+ val isContact: Boolean
+ get() = !isGroup
+
+ fun contactIdentifier(): String {
+ if (!isContact && !isOpenGroup) {
+ if (isGroup) throw AssertionError("Not e164, is group")
+ throw AssertionError("Not e164, unknown")
+ }
+ return address
+ }
+
+ override fun toString(): String {
+ return address
+ }
+
+ fun serialize(): String {
+ return address
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ return if (other == null || other !is Address) false else address == other.address
+ }
+
+ override fun hashCode(): Int {
+ return address.hashCode()
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ override fun writeToParcel(dest: Parcel, flags: Int) {
+ dest.writeString(address)
+ }
+
+ override fun compareTo(other: Address?): Int {
+ return address.compareTo(other?.address!!)
+ }
+
+ @VisibleForTesting
+ class ExternalAddressFormatter internal constructor(localCountryCode: String, countryCode: Boolean) {
+ private val localNumber: Optional
+ private val localCountryCode: String
+ private val ALPHA_PATTERN = Pattern.compile("[a-zA-Z]")
+ fun format(number: String?): String {
+ return number ?: "Unknown"
+ }
+
+ private fun parseAreaCode(e164Number: String, countryCode: Int): String? {
+ when (countryCode) {
+ 1 -> return e164Number.substring(2, 5)
+ 55 -> return e164Number.substring(3, 5)
+ }
+ return null
+ }
+
+ private fun applyAreaCodeRules(localNumber: Optional, testNumber: String): String {
+ if (!localNumber.isPresent || !localNumber.get().areaCode.isPresent) {
+ return testNumber
+ }
+ val matcher: Matcher
+ when (localNumber.get().countryCode) {
+ 1 -> {
+ matcher = US_NO_AREACODE.matcher(testNumber)
+ if (matcher.matches()) {
+ return localNumber.get().areaCode.toString() + matcher.group()
+ }
+ }
+ 55 -> {
+ matcher = BR_NO_AREACODE.matcher(testNumber)
+ if (matcher.matches()) {
+ return localNumber.get().areaCode.toString() + matcher.group()
+ }
+ }
+ }
+ return testNumber
+ }
+
+ private class PhoneNumber internal constructor(val e164Number: String, val countryCode: Int, areaCode: String?) {
+ val areaCode: Optional
+
+ init {
+ this.areaCode = Optional.fromNullable(areaCode)
+ }
+ }
+
+ companion object {
+ private val TAG = ExternalAddressFormatter::class.java.simpleName
+ private val SHORT_COUNTRIES: HashSet = object : HashSet() {
+ init {
+ add("NU")
+ add("TK")
+ add("NC")
+ add("AC")
+ }
+ }
+ private val US_NO_AREACODE = Pattern.compile("^(\\d{7})$")
+ private val BR_NO_AREACODE = Pattern.compile("^(9?\\d{8})$")
+ }
+
+ init {
+ localNumber = Optional.absent()
+ this.localCountryCode = localCountryCode
+ }
+ }
+
+ companion object {
+ val CREATOR: Parcelable.Creator = object : Parcelable.Creator {
+ override fun createFromParcel(`in`: Parcel): Address {
+ return Address(`in`)
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+ }
+ val UNKNOWN = Address("Unknown")
+ private val TAG = Address::class.java.simpleName
+ private val cachedFormatter = AtomicReference>()
+ fun fromSerialized(serialized: String): Address {
+ return Address(serialized)
+ }
+
+ fun fromExternal(context: Context, external: String?): Address {
+ return fromSerialized(external!!)
+ }
+
+ fun fromSerializedList(serialized: String, delimiter: Char): List {
+ val escapedAddresses = split(serialized, delimiter)
+ val addresses: MutableList = LinkedList()
+ for (escapedAddress in escapedAddresses) {
+ addresses.add(fromSerialized(unescape(escapedAddress, delimiter)))
+ }
+ return addresses
+ }
+
+ fun toSerializedList(addresses: List, delimiter: Char): String {
+ Collections.sort(addresses)
+ val escapedAddresses: MutableList = LinkedList()
+ for (address in addresses) {
+ escapedAddresses.add(escape(address.serialize(), delimiter))
+ }
+ return Util.join(escapedAddresses, delimiter.toString() + "")
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/libsession/src/main/java/org/session/libsession/messaging/threads/GroupRecord.kt b/libsession/src/main/java/org/session/libsession/messaging/threads/GroupRecord.kt
new file mode 100644
index 0000000000..ce19c18db8
--- /dev/null
+++ b/libsession/src/main/java/org/session/libsession/messaging/threads/GroupRecord.kt
@@ -0,0 +1,36 @@
+package org.session.libsession.messaging.threads
+
+import android.text.TextUtils
+import org.session.libsession.utilities.GroupUtil
+import java.io.IOException
+import java.util.*
+
+class GroupRecord(
+ val encodedId: String, val title: String, members: String?, val avatar: ByteArray,
+ val avatarId: Long, val avatarKey: ByteArray, val avatarContentType: String,
+ val relay: String, val isActive: Boolean, val avatarDigest: ByteArray, val isMms: Boolean, val url: String, admins: String?,
+) {
+ var members: List = LinkedList()
+ var admins: List = LinkedList()
+ fun getId(): ByteArray {
+ return try {
+ GroupUtil.getDecodedGroupIDAsData(encodedId.toByteArray())
+ } catch (ioe: IOException) {
+ throw AssertionError(ioe)
+ }
+ }
+
+ val isOpenGroup: Boolean
+ get() = Address.fromSerialized(encodedId).isOpenGroup
+ val isClosedGroup: Boolean
+ get() = Address.fromSerialized(encodedId).isClosedGroup
+
+ init {
+ if (!TextUtils.isEmpty(members)) {
+ this.members = Address.fromSerializedList(members!!, ',')
+ }
+ if (!TextUtils.isEmpty(admins)) {
+ this.admins = Address.fromSerializedList(admins!!, ',')
+ }
+ }
+}
\ No newline at end of file
diff --git a/libsession/src/main/java/org/session/libsession/utilities/LKGroupUtilities.kt b/libsession/src/main/java/org/session/libsession/utilities/LKGroupUtilities.kt
deleted file mode 100644
index 5c47e0b53f..0000000000
--- a/libsession/src/main/java/org/session/libsession/utilities/LKGroupUtilities.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package org.session.libsession.utilities
-
-object LKGroupUtilities {
- const val CLOSED_GROUP_PREFIX = "__textsecure_group__!"
- const val MMS_GROUP_PREFIX = "__signal_mms_group__!"
- const val OPEN_GROUP_PREFIX = "__loki_public_chat_group__!"
-
- fun getEncodedOpenGroupID(groupID: String): String {
- return OPEN_GROUP_PREFIX + groupID
- }
-
- fun getEncodedOpenGroupIDAsData(groupID: String): ByteArray {
- return (OPEN_GROUP_PREFIX + groupID).toByteArray()
- }
-
- fun getEncodedClosedGroupID(groupID: String): String {
- return CLOSED_GROUP_PREFIX + groupID
- }
-
- fun getEncodedClosedGroupIDAsData(groupID: String): ByteArray {
- return (CLOSED_GROUP_PREFIX + groupID).toByteArray()
- }
-
- fun getEncodedMMSGroupID(groupID: String): String {
- return MMS_GROUP_PREFIX + groupID
- }
-
- fun getEncodedMMSGroupIDAsData(groupID: String): ByteArray {
- return (MMS_GROUP_PREFIX + groupID).toByteArray()
- }
-
- fun getEncodedGroupID(groupID: ByteArray): String {
- return groupID.toString()
- }
-
- fun getDecodedGroupID(groupID: ByteArray): String {
- val encodedGroupID = groupID.toString()
- if (encodedGroupID.split("!").count() > 1) {
- return encodedGroupID.split("!")[1]
- }
- return encodedGroupID.split("!")[0]
- }
-
- fun getDecodedGroupIDAsData(groupID: ByteArray): ByteArray {
- return getDecodedGroupID(groupID).toByteArray()
- }
-}
\ No newline at end of file