From bb6a6987df196815748a04d3dccfe1f146e072b2 Mon Sep 17 00:00:00 2001 From: Robin Linden Date: Sun, 9 Feb 2025 00:35:01 +0100 Subject: [PATCH] Use the PublicKey type for public keys in the database This means a lot of conversions go away. --- .../kotlin/ui/AvatarFactoryTest.kt | 9 ++-- atox/src/main/kotlin/ActionReceiver.kt | 10 ++-- atox/src/main/kotlin/ToxService.kt | 4 +- .../main/kotlin/tox/EventListenerCallbacks.kt | 21 ++++----- atox/src/main/kotlin/ui/AvatarFactory.kt | 8 ++-- atox/src/main/kotlin/ui/AvatarImageView.kt | 5 +- atox/src/main/kotlin/ui/NotificationHelper.kt | 35 ++++++++------ .../ui/addcontact/AddContactFragment.kt | 4 +- .../ui/addcontact/AddContactViewModel.kt | 8 ++-- atox/src/main/kotlin/ui/chat/ChatAdapter.kt | 5 +- atox/src/main/kotlin/ui/chat/ChatFragment.kt | 16 +++---- atox/src/main/kotlin/ui/chat/ChatViewModel.kt | 4 +- .../kotlin/ui/contactlist/ContactAdapter.kt | 6 +-- .../ui/contactlist/ContactListFragment.kt | 9 ++-- .../ui/contactlist/ContactListViewModel.kt | 2 +- .../contactprofile/ContactProfileFragment.kt | 2 +- .../ui/createprofile/CreateProfileFragment.kt | 5 +- .../ui/friendrequest/FriendRequestFragment.kt | 2 +- core/build.gradle.kts | 1 + .../androidTest/kotlin/db/ContactDaoTest.kt | 7 +-- .../kotlin/db/DatabaseMigrationTest.kt | 33 ++++++------- core/src/androidTest/kotlin/db/UserDaoTest.kt | 5 +- core/src/main/kotlin/db/ContactDao.kt | 47 ++++++++++--------- core/src/main/kotlin/db/Converters.kt | 11 ++++- core/src/main/kotlin/db/FileTransferDao.kt | 7 +-- core/src/main/kotlin/db/FriendRequestDao.kt | 7 +-- core/src/main/kotlin/db/MessageDao.kt | 11 +++-- core/src/main/kotlin/db/UserDao.kt | 27 ++++++----- .../kotlin/repository/ContactRepository.kt | 25 +++++----- .../repository/FileTransferRepository.kt | 5 +- .../repository/FriendRequestRepository.kt | 5 +- .../kotlin/repository/MessageRepository.kt | 11 +++-- .../main/kotlin/repository/UserRepository.kt | 18 +++---- core/src/main/kotlin/vo/Contact.kt | 4 +- core/src/main/kotlin/vo/FileTransfer.kt | 4 +- core/src/main/kotlin/vo/FriendRequest.kt | 4 +- core/src/main/kotlin/vo/Message.kt | 4 +- core/src/main/kotlin/vo/User.kt | 4 +- domain/src/main/kotlin/feature/CallManager.kt | 6 +-- domain/src/main/kotlin/feature/ChatManager.kt | 20 ++++---- .../src/main/kotlin/feature/ContactManager.kt | 12 ++--- .../src/main/kotlin/feature/ExportManager.kt | 9 ++-- .../kotlin/feature/FileTransferManager.kt | 32 ++++++------- .../kotlin/feature/FriendRequestManager.kt | 4 +- domain/src/main/kotlin/feature/UserManager.kt | 14 +++--- domain/src/main/kotlin/tox/Tox.kt | 6 +-- .../src/main/kotlin/tox/ToxAvEventListener.kt | 14 +++--- .../src/main/kotlin/tox/ToxEventListener.kt | 33 ++++++------- domain/src/test/kotlin/tox/ToxTypesTest.kt | 2 +- 49 files changed, 289 insertions(+), 258 deletions(-) diff --git a/atox/src/androidTest/kotlin/ui/AvatarFactoryTest.kt b/atox/src/androidTest/kotlin/ui/AvatarFactoryTest.kt index 857a55a66..f9f6dd671 100644 --- a/atox/src/androidTest/kotlin/ui/AvatarFactoryTest.kt +++ b/atox/src/androidTest/kotlin/ui/AvatarFactoryTest.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Robin Lindén +// SPDX-FileCopyrightText: 2022-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -7,8 +7,11 @@ package ltd.evilcorp.atox.ui import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import kotlin.test.Test +import ltd.evilcorp.core.vo.PublicKey import org.junit.runner.RunWith +private val pk = PublicKey("123") + @RunWith(AndroidJUnit4::class) class AvatarFactoryTest { @Test @@ -16,7 +19,7 @@ class AvatarFactoryTest { AvatarFactory.create( InstrumentationRegistry.getInstrumentation().targetContext.resources, name = "", - publicKey = "123", + pk = pk, ) } @@ -25,7 +28,7 @@ class AvatarFactoryTest { AvatarFactory.create( InstrumentationRegistry.getInstrumentation().targetContext.resources, name = "a ", - publicKey = "123", + pk = pk, ) } } diff --git a/atox/src/main/kotlin/ActionReceiver.kt b/atox/src/main/kotlin/ActionReceiver.kt index 67fe0562d..042884fa8 100644 --- a/atox/src/main/kotlin/ActionReceiver.kt +++ b/atox/src/main/kotlin/ActionReceiver.kt @@ -76,7 +76,7 @@ class ActionReceiver : BroadcastReceiver() { Log.e(TAG, "Got intent without required key $KEY_CONTACT_PK $intent") return@launch } - if (!contactRepository.exists(pk.string())) { + if (!contactRepository.exists(pk)) { notificationHelper.dismissNotifications(pk) notificationHelper.dismissCallNotification(pk) return@launch @@ -84,10 +84,10 @@ class ActionReceiver : BroadcastReceiver() { RemoteInput.getResultsFromIntent(intent)?.let { results -> results.getCharSequence(KEY_TEXT_REPLY)?.toString()?.let { input -> - contactRepository.setHasUnreadMessages(pk.string(), false) + contactRepository.setHasUnreadMessages(pk, false) chatManager.sendMessage(pk, input) notificationHelper.showMessageNotification( - Contact(pk.string(), tox.getName()), + Contact(pk, tox.getName()), input, outgoing = true, ) @@ -103,7 +103,7 @@ class ActionReceiver : BroadcastReceiver() { } Action.CallIgnore -> callManager.removePendingCall(pk) Action.MarkAsRead -> { - contactRepository.setHasUnreadMessages(pk.string(), false) + contactRepository.setHasUnreadMessages(pk, false) notificationHelper.dismissNotifications(pk) } null -> Log.e(TAG, "Missing action in intent $intent") @@ -117,7 +117,7 @@ class ActionReceiver : BroadcastReceiver() { it } else { Log.e(TAG, "Unable to get contact ${pk.fingerprint()} for call notification") - Contact(publicKey = pk.string(), name = pk.fingerprint()) + Contact(publicKey = pk, name = pk.fingerprint()) } } diff --git a/atox/src/main/kotlin/ToxService.kt b/atox/src/main/kotlin/ToxService.kt index 05e55982c..692e76bfd 100644 --- a/atox/src/main/kotlin/ToxService.kt +++ b/atox/src/main/kotlin/ToxService.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2024 Robin Lindén +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // SPDX-FileCopyrightText: 2021-2022 aTox contributors // // SPDX-License-Identifier: GPL-3.0-only @@ -139,7 +139,7 @@ class ToxService : LifecycleService() { } lifecycleScope.launch(Dispatchers.Default) { - userRepository.get(tox.publicKey.string()) + userRepository.get(tox.publicKey) .filterNotNull() .filter { it.connectionStatus != connectionStatus } .flowWithLifecycle(lifecycle) diff --git a/atox/src/main/kotlin/tox/EventListenerCallbacks.kt b/atox/src/main/kotlin/tox/EventListenerCallbacks.kt index 3b61968aa..02e46fdb8 100644 --- a/atox/src/main/kotlin/tox/EventListenerCallbacks.kt +++ b/atox/src/main/kotlin/tox/EventListenerCallbacks.kt @@ -52,9 +52,6 @@ private fun isImage(filename: String) = try { false } -private const val FINGERPRINT_LEN = 8 -private fun String.fingerprint() = this.take(FINGERPRINT_LEN) - @Singleton class EventListenerCallbacks @Inject constructor( private val ctx: Context, @@ -73,7 +70,7 @@ class EventListenerCallbacks @Inject constructor( private var audioPlayer: AudioPlayer? = null private val scope = CoroutineScope(Dispatchers.Default) - private suspend fun tryGetContact(pk: String, tag: String) = contactRepository.get(pk).firstOrNull().let { + private suspend fun tryGetContact(pk: PublicKey, tag: String) = contactRepository.get(pk).firstOrNull().let { if (it == null) Log.e(TAG, "$tag -> unable to get contact for ${pk.fingerprint()}") it } @@ -147,7 +144,7 @@ class EventListenerCallbacks @Inject constructor( } fileRecvHandler = { publicKey, fileNo, kind, fileSize, filename -> - val name = if (kind == FileKind.Avatar.ordinal) publicKey else filename + val name = if (kind == FileKind.Avatar.ordinal) publicKey.string() else filename val id = fileTransferManager.add(FileTransfer(publicKey, fileNo, kind, fileSize, name, outgoing = false)) @@ -168,16 +165,16 @@ class EventListenerCallbacks @Inject constructor( } } - fileRecvControlHandler = { publicKey: String, fileNo: Int, control: ToxFileControl -> - fileTransferManager.setStatus(publicKey, fileNo, control) + fileRecvControlHandler = { pk: PublicKey, fileNo: Int, control: ToxFileControl -> + fileTransferManager.setStatus(pk, fileNo, control) } - fileChunkRequestHandler = { publicKey: String, fileNo: Int, position: Long, length: Int -> - fileTransferManager.sendChunk(publicKey, fileNo, position, length) + fileChunkRequestHandler = { pk: PublicKey, fileNo: Int, position: Long, length: Int -> + fileTransferManager.sendChunk(pk, fileNo, position, length) } selfConnectionStatusHandler = { status -> - userRepository.updateConnection(tox.publicKey.string(), status) + userRepository.updateConnection(tox.publicKey, status) } friendTypingHandler = { publicKey, isTyping -> @@ -201,8 +198,8 @@ class EventListenerCallbacks @Inject constructor( audioPlayer?.stop() audioPlayer?.release() audioPlayer = null - notificationHelper.dismissCallNotification(PublicKey(pk)) - callManager.endCall(PublicKey(pk)) + notificationHelper.dismissCallNotification(pk) + callManager.endCall(pk) } } diff --git a/atox/src/main/kotlin/ui/AvatarFactory.kt b/atox/src/main/kotlin/ui/AvatarFactory.kt index fa09a2983..80274590e 100644 --- a/atox/src/main/kotlin/ui/AvatarFactory.kt +++ b/atox/src/main/kotlin/ui/AvatarFactory.kt @@ -1,4 +1,5 @@ -// SPDX-FileCopyrightText: 2019-2022 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén +// SPDX-FileCopyrightText: 2021-2022 aTox contributors // // SPDX-License-Identifier: GPL-3.0-only @@ -14,6 +15,7 @@ import android.graphics.RectF import android.graphics.Typeface import kotlin.math.abs import ltd.evilcorp.atox.R +import ltd.evilcorp.core.vo.PublicKey internal object AvatarFactory { @@ -27,7 +29,7 @@ internal object AvatarFactory { fun create( resources: Resources, name: String, - publicKey: String, + pk: PublicKey, size: Px = Px(resources.getDimension(R.dimen.default_avatar_size).toInt()), ): Bitmap { val defaultAvatarSize = resources.getDimension(R.dimen.default_avatar_size) @@ -37,7 +39,7 @@ internal object AvatarFactory { val canvas = Canvas(bitmap) val rect = RectF(0f, 0f, bitmap.width.toFloat(), bitmap.height.toFloat()) val colors = resources.getIntArray(R.array.contactBackgrounds) - val backgroundPaint = Paint().apply { color = colors[abs(publicKey.hashCode()).rem(colors.size)] } + val backgroundPaint = Paint().apply { color = colors[abs(pk.hashCode()).rem(colors.size)] } val textPaint = Paint().apply { color = Color.WHITE diff --git a/atox/src/main/kotlin/ui/AvatarImageView.kt b/atox/src/main/kotlin/ui/AvatarImageView.kt index ed37ace2a..d9e79a9c4 100644 --- a/atox/src/main/kotlin/ui/AvatarImageView.kt +++ b/atox/src/main/kotlin/ui/AvatarImageView.kt @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: 2022 aTox contributors -// SPDX-FileCopyrightText: 2022-2024 Robin Lindén +// SPDX-FileCopyrightText: 2022-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -21,6 +21,7 @@ import kotlin.math.sqrt import ltd.evilcorp.atox.R import ltd.evilcorp.core.vo.ConnectionStatus import ltd.evilcorp.core.vo.Contact +import ltd.evilcorp.core.vo.PublicKey import ltd.evilcorp.core.vo.UserStatus private const val STATUS_INDICATOR_SIZE_RATIO_WITH_AVATAR = 12f / 50 @@ -50,7 +51,7 @@ class AvatarImageView @JvmOverloads constructor(context: Context, attrs: Attribu } private var name = "" - private var publicKey = "" + private var publicKey = PublicKey("") private var avatarUri = "" fun setFrom(contact: Contact) { diff --git a/atox/src/main/kotlin/ui/NotificationHelper.kt b/atox/src/main/kotlin/ui/NotificationHelper.kt index bb96a2154..ae76281d2 100644 --- a/atox/src/main/kotlin/ui/NotificationHelper.kt +++ b/atox/src/main/kotlin/ui/NotificationHelper.kt @@ -138,7 +138,7 @@ class NotificationHelper @Inject constructor(private val context: Context) { .setSmallIcon(android.R.drawable.sym_action_chat) .setContentTitle(contact.name.ifEmpty { context.getText(R.string.contact_default_name) }) .setContentText(message) - .setContentIntent(deepLinkToChat(PublicKey(contact.publicKey))) + .setContentIntent(deepLinkToChat(contact.publicKey)) .setAutoCancel(true) .setSilent(silent) @@ -151,13 +151,15 @@ class NotificationHelper @Inject constructor(private val context: Context) { val chatPartner = Person.Builder() .setName(contact.name.ifEmpty { context.getText(R.string.contact_default_name) }) - .setKey(if (outgoing) "myself" else contact.publicKey) + .setKey(if (outgoing) "myself" else contact.publicKey.string()) .setIcon(icon) .setImportant(true) .build() val style = - notifierOld.activeNotifications.find { it.notification.group == contact.publicKey }?.notification?.let { + notifierOld.activeNotifications.find { + it.notification.group == contact.publicKey.string() + }?.notification?.let { NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(it) } ?: NotificationCompat.MessagingStyle(chatPartner) @@ -167,7 +169,7 @@ class NotificationHelper @Inject constructor(private val context: Context) { notificationBuilder .setStyle(style) - .setGroup(contact.publicKey) + .setGroup(contact.publicKey.string()) } // I can't find it in the documentation for RemoteInput or anything, but per @@ -182,7 +184,10 @@ class NotificationHelper @Inject constructor(private val context: Context) { PendingIntentCompat.getBroadcast( context, contact.publicKey.hashCode(), - Intent(context, ActionReceiver::class.java).putExtra(KEY_CONTACT_PK, contact.publicKey), + Intent(context, ActionReceiver::class.java).putExtra( + KEY_CONTACT_PK, + contact.publicKey.string(), + ), PendingIntent.FLAG_UPDATE_CURRENT, mutable = true, ), @@ -202,7 +207,7 @@ class NotificationHelper @Inject constructor(private val context: Context) { .Builder( IconCompat.createWithResource(context, R.drawable.ic_send), context.getString(R.string.reply), - deepLinkToChat(PublicKey(contact.publicKey), focusMessageBox = true), + deepLinkToChat(contact.publicKey, focusMessageBox = true), ) .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) .build(), @@ -217,7 +222,7 @@ class NotificationHelper @Inject constructor(private val context: Context) { context, "${contact.publicKey}_mark_as_read".hashCode(), Intent(context, ActionReceiver::class.java) - .putExtra(KEY_CONTACT_PK, contact.publicKey) + .putExtra(KEY_CONTACT_PK, contact.publicKey.string()) .putExtra(KEY_ACTION, Action.MarkAsRead), PendingIntent.FLAG_UPDATE_CURRENT, ), @@ -266,7 +271,7 @@ class NotificationHelper @Inject constructor(private val context: Context) { return } - dismissCallNotification(PublicKey(contact.publicKey)) + dismissCallNotification(contact.publicKey) val notificationBuilder = NotificationCompat.Builder(context, CALL) .setCategory(NotificationCompat.CATEGORY_CALL) .setSmallIcon(android.R.drawable.ic_menu_call) @@ -282,8 +287,8 @@ class NotificationHelper @Inject constructor(private val context: Context) { .setContentIntent( NavDeepLinkBuilder(context) .setGraph(R.navigation.nav_graph) - .addDestination(R.id.chatFragment, bundleOf(CONTACT_PUBLIC_KEY to contact.publicKey)) - .addDestination(R.id.callFragment, bundleOf(CONTACT_PUBLIC_KEY to contact.publicKey)) + .addDestination(R.id.chatFragment, bundleOf(CONTACT_PUBLIC_KEY to contact.publicKey.string())) + .addDestination(R.id.callFragment, bundleOf(CONTACT_PUBLIC_KEY to contact.publicKey.string())) .createPendingIntent(), ) .addAction( @@ -295,7 +300,7 @@ class NotificationHelper @Inject constructor(private val context: Context) { context, "${contact.publicKey}_end_call".hashCode(), Intent(context, ActionReceiver::class.java) - .putExtra(KEY_CONTACT_PK, contact.publicKey) + .putExtra(KEY_CONTACT_PK, contact.publicKey.string()) .putExtra(KEY_ACTION, Action.CallEnd), PendingIntent.FLAG_UPDATE_CURRENT, ), @@ -323,7 +328,7 @@ class NotificationHelper @Inject constructor(private val context: Context) { .setSmallIcon(android.R.drawable.ic_menu_call) .setContentTitle(context.getString(R.string.incoming_call)) .setContentText(context.getString(R.string.incoming_call_from, c.name)) - .setContentIntent(deepLinkToChat(PublicKey(c.publicKey))) + .setContentIntent(deepLinkToChat(c.publicKey)) .addAction( NotificationCompat.Action .Builder( @@ -333,7 +338,7 @@ class NotificationHelper @Inject constructor(private val context: Context) { context, "${c.publicKey}_accept_call".hashCode(), Intent(context, ActionReceiver::class.java) - .putExtra(KEY_CONTACT_PK, c.publicKey) + .putExtra(KEY_CONTACT_PK, c.publicKey.string()) .putExtra(KEY_ACTION, Action.CallAccept), PendingIntent.FLAG_UPDATE_CURRENT, ), @@ -350,7 +355,7 @@ class NotificationHelper @Inject constructor(private val context: Context) { context, "${c.publicKey}_reject_call".hashCode(), Intent(context, ActionReceiver::class.java) - .putExtra(KEY_CONTACT_PK, c.publicKey) + .putExtra(KEY_CONTACT_PK, c.publicKey.string()) .putExtra(KEY_ACTION, Action.CallReject), PendingIntent.FLAG_UPDATE_CURRENT, ), @@ -362,7 +367,7 @@ class NotificationHelper @Inject constructor(private val context: Context) { context, "${c.publicKey}_ignore_call".hashCode(), Intent(context, ActionReceiver::class.java) - .putExtra(KEY_CONTACT_PK, c.publicKey) + .putExtra(KEY_CONTACT_PK, c.publicKey.string()) .putExtra(KEY_ACTION, Action.CallIgnore), PendingIntent.FLAG_UPDATE_CURRENT, ), diff --git a/atox/src/main/kotlin/ui/addcontact/AddContactFragment.kt b/atox/src/main/kotlin/ui/addcontact/AddContactFragment.kt index 684721027..73ab93888 100644 --- a/atox/src/main/kotlin/ui/addcontact/AddContactFragment.kt +++ b/atox/src/main/kotlin/ui/addcontact/AddContactFragment.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2024 Robin Lindén +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // SPDX-FileCopyrightText: 2020 aTox contributors // // SPDX-License-Identifier: GPL-3.0-only @@ -84,7 +84,7 @@ class AddContactFragment : BaseFragment(FragmentAddCo } if (toxId.error == null) { - if (contacts.find { it.publicKey == input.toPublicKey().string() } != null) { + if (contacts.find { it.publicKey == input.toPublicKey() } != null) { toxId.error = getString(R.string.tox_id_error_already_exists) } } diff --git a/atox/src/main/kotlin/ui/addcontact/AddContactViewModel.kt b/atox/src/main/kotlin/ui/addcontact/AddContactViewModel.kt index 3eeb96ea6..681e46d76 100644 --- a/atox/src/main/kotlin/ui/addcontact/AddContactViewModel.kt +++ b/atox/src/main/kotlin/ui/addcontact/AddContactViewModel.kt @@ -1,4 +1,5 @@ -// SPDX-FileCopyrightText: 2019-2022 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén +// SPDX-FileCopyrightText: 2022 aTox contributors // // SPDX-License-Identifier: GPL-3.0-only @@ -16,6 +17,7 @@ import ltd.evilcorp.core.repository.MessageRepository import ltd.evilcorp.core.vo.Contact import ltd.evilcorp.core.vo.Message import ltd.evilcorp.core.vo.MessageType +import ltd.evilcorp.core.vo.PublicKey import ltd.evilcorp.core.vo.Sender import ltd.evilcorp.domain.feature.ContactManager import ltd.evilcorp.domain.tox.Tox @@ -35,7 +37,7 @@ class AddContactViewModel @Inject constructor( fun isToxRunning() = tox.started fun tryLoadTox(): Boolean = toxStarter.tryLoadTox(null) == ToxSaveStatus.Ok - private fun addToChatLog(publicKey: String, message: String) = scope.launch { + private fun addToChatLog(publicKey: PublicKey, message: String) = scope.launch { messageRepository.add( Message( publicKey, @@ -50,6 +52,6 @@ class AddContactViewModel @Inject constructor( fun addContact(toxId: ToxID, message: String) { contactManager.add(toxId, message) - addToChatLog(toxId.toPublicKey().string(), message) + addToChatLog(toxId.toPublicKey(), message) } } diff --git a/atox/src/main/kotlin/ui/chat/ChatAdapter.kt b/atox/src/main/kotlin/ui/chat/ChatAdapter.kt index 39887d47a..3fb521792 100644 --- a/atox/src/main/kotlin/ui/chat/ChatAdapter.kt +++ b/atox/src/main/kotlin/ui/chat/ChatAdapter.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2024 Robin Lindén +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -28,6 +28,7 @@ import ltd.evilcorp.atox.R import ltd.evilcorp.core.vo.FileTransfer import ltd.evilcorp.core.vo.Message import ltd.evilcorp.core.vo.MessageType +import ltd.evilcorp.core.vo.PublicKey import ltd.evilcorp.core.vo.Sender import ltd.evilcorp.core.vo.isComplete import ltd.evilcorp.core.vo.isRejected @@ -158,7 +159,7 @@ class ChatAdapter(private val inflater: LayoutInflater, private val resources: R val message = messages[position] val fileTransfer = fileTransfers.find { it.id == message.correlationId } ?: run { Log.e(TAG, "Unable to find ft ${message.correlationId} for ${message.publicKey} required for view") - FileTransfer("", 0, 0, 0, "", message.sender == Sender.Sent) + FileTransfer(PublicKey(""), 0, 0, 0, "", message.sender == Sender.Sent) } val view: View diff --git a/atox/src/main/kotlin/ui/chat/ChatFragment.kt b/atox/src/main/kotlin/ui/chat/ChatFragment.kt index 6ef8cc8e7..23623cc45 100644 --- a/atox/src/main/kotlin/ui/chat/ChatFragment.kt +++ b/atox/src/main/kotlin/ui/chat/ChatFragment.kt @@ -67,7 +67,7 @@ class OpenMultiplePersistableDocuments : ActivityResultContracts.OpenMultipleDoc class ChatFragment : BaseFragment(FragmentChatBinding::inflate) { private val viewModel: ChatViewModel by viewModels { vmFactory } - private lateinit var contactPubKey: String + private var contactPubKey = PublicKey("") private var contactName = "" private var selectedFt: Int = Int.MIN_VALUE private var fts: List = listOf() @@ -85,7 +85,7 @@ class ChatFragment : BaseFragment(FragmentChatBinding::infl private val attachFilesLauncher = registerForActivityResult(OpenMultiplePersistableDocuments()) { files -> - viewModel.setActiveChat(PublicKey(contactPubKey)) + viewModel.setActiveChat(contactPubKey) for (file in files) { activity?.contentResolver?.takePersistableUriPermission(file, Intent.FLAG_GRANT_READ_URI_PERMISSION) viewModel.createFt(file) @@ -93,8 +93,8 @@ class ChatFragment : BaseFragment(FragmentChatBinding::infl } override fun onViewCreated(view: View, savedInstanceState: Bundle?): Unit = binding.run { - contactPubKey = requireStringArg(CONTACT_PUBLIC_KEY) - viewModel.setActiveChat(PublicKey(contactPubKey)) + contactPubKey = PublicKey(requireStringArg(CONTACT_PUBLIC_KEY)) + viewModel.setActiveChat(contactPubKey) ViewCompat.setOnApplyWindowInsetsListener(view) { _, compat -> val insets = compat.getInsets(WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.ime()) @@ -196,7 +196,7 @@ class ChatFragment : BaseFragment(FragmentChatBinding::infl WindowInsetsControllerCompat(requireActivity().window, view).hide(WindowInsetsCompat.Type.ime()) findNavController().navigate( R.id.action_chatFragment_to_contactProfileFragment, - bundleOf(CONTACT_PUBLIC_KEY to contactPubKey), + bundleOf(CONTACT_PUBLIC_KEY to contactPubKey.string()), ) } @@ -248,7 +248,7 @@ class ChatFragment : BaseFragment(FragmentChatBinding::infl } viewModel.ongoingCall.observe(viewLifecycleOwner) { - if (it is CallState.InCall && it.publicKey.string() == contactPubKey) { + if (it is CallState.InCall && it.publicKey == contactPubKey) { ongoingCall.container.visibility = View.VISIBLE if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { ongoingCall.duration.visibility = View.VISIBLE @@ -345,7 +345,7 @@ class ChatFragment : BaseFragment(FragmentChatBinding::infl } override fun onResume() = binding.run { - viewModel.setActiveChat(PublicKey(contactPubKey)) + viewModel.setActiveChat(contactPubKey) viewModel.setTyping(outgoingMessage.text.isNotEmpty()) super.onResume() } @@ -442,7 +442,7 @@ class ChatFragment : BaseFragment(FragmentChatBinding::infl view?.let { WindowInsetsControllerCompat(requireActivity().window, it).hide(WindowInsetsCompat.Type.ime()) } findNavController().navigate( R.id.action_chatFragment_to_callFragment, - bundleOf(CONTACT_PUBLIC_KEY to contactPubKey), + bundleOf(CONTACT_PUBLIC_KEY to contactPubKey.string()), ) } } diff --git a/atox/src/main/kotlin/ui/chat/ChatViewModel.kt b/atox/src/main/kotlin/ui/chat/ChatViewModel.kt index 70e04fe34..d2a2bb221 100644 --- a/atox/src/main/kotlin/ui/chat/ChatViewModel.kt +++ b/atox/src/main/kotlin/ui/chat/ChatViewModel.kt @@ -110,7 +110,7 @@ class ChatViewModel @Inject constructor( publicKey = pk notificationHelper.dismissNotifications(publicKey) - chatManager.activeChat = publicKey.string() + chatManager.activeChat = publicKey } fun setTyping(typing: Boolean) { @@ -165,7 +165,7 @@ class ChatViewModel @Inject constructor( } } - fun backupHistory(publicKey: String, locationSave: Uri) = scope.launch { + fun backupHistory(publicKey: PublicKey, locationSave: Uri) = scope.launch { val backupContent = exportManager.generateExportMessagesJString(publicKey) launch(Dispatchers.IO) { try { diff --git a/atox/src/main/kotlin/ui/contactlist/ContactAdapter.kt b/atox/src/main/kotlin/ui/contactlist/ContactAdapter.kt index 2581d39a2..aaa529eeb 100644 --- a/atox/src/main/kotlin/ui/contactlist/ContactAdapter.kt +++ b/atox/src/main/kotlin/ui/contactlist/ContactAdapter.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2024 Robin Lindén +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // SPDX-FileCopyrightText: 2021-2022 aTox contributors // // SPDX-License-Identifier: GPL-3.0-only @@ -59,7 +59,7 @@ class ContactAdapter(private val inflater: LayoutInflater, private val context: } friendRequests[position].run { - vh.publicKey.text = publicKey + vh.publicKey.text = publicKey.string() vh.message.text = message } @@ -81,7 +81,7 @@ class ContactAdapter(private val inflater: LayoutInflater, private val context: contacts[position - friendRequests.size].run { name = name.ifEmpty { context.getString(R.string.contact_default_name) } - val shortId = publicKey.take(8) + val shortId = publicKey.fingerprint() vh.publicKey.text = String.format("%s %s", shortId.take(4), shortId.takeLast(4)) vh.name.text = name vh.lastMessage.text = if (lastMessage != 0L) { diff --git a/atox/src/main/kotlin/ui/contactlist/ContactListFragment.kt b/atox/src/main/kotlin/ui/contactlist/ContactListFragment.kt index 7e375a2e4..184b1bf60 100644 --- a/atox/src/main/kotlin/ui/contactlist/ContactListFragment.kt +++ b/atox/src/main/kotlin/ui/contactlist/ContactListFragment.kt @@ -49,7 +49,6 @@ import ltd.evilcorp.atox.vmFactory import ltd.evilcorp.core.vo.ConnectionStatus import ltd.evilcorp.core.vo.Contact import ltd.evilcorp.core.vo.FriendRequest -import ltd.evilcorp.core.vo.PublicKey import ltd.evilcorp.core.vo.User import ltd.evilcorp.domain.tox.ToxSaveStatus @@ -258,7 +257,7 @@ class ContactListFragment : ), ) .setPositiveButton(R.string.delete) { _, _ -> - viewModel.deleteContact(PublicKey(contact.publicKey)) + viewModel.deleteContact(contact.publicKey) } .setNegativeButton(android.R.string.cancel, null).show() } @@ -375,16 +374,16 @@ class ContactListFragment : private fun openChat(contact: Contact) = findNavController().navigate( R.id.action_contactListFragment_to_chatFragment, - bundleOf(CONTACT_PUBLIC_KEY to contact.publicKey), + bundleOf(CONTACT_PUBLIC_KEY to contact.publicKey.string()), ) private fun openFriendRequest(friendRequest: FriendRequest) = findNavController().navigate( R.id.action_contactListFragment_to_friendRequestFragment, - bundleOf(FRIEND_REQUEST_PUBLIC_KEY to friendRequest.publicKey), + bundleOf(FRIEND_REQUEST_PUBLIC_KEY to friendRequest.publicKey.string()), ) private fun openProfile(contact: Contact) = findNavController().navigate( R.id.action_contactListFragment_to_contactProfileFragment, - bundleOf(CONTACT_PUBLIC_KEY to contact.publicKey), + bundleOf(CONTACT_PUBLIC_KEY to contact.publicKey.string()), ) } diff --git a/atox/src/main/kotlin/ui/contactlist/ContactListViewModel.kt b/atox/src/main/kotlin/ui/contactlist/ContactListViewModel.kt index b3c8d8538..28188bfcf 100644 --- a/atox/src/main/kotlin/ui/contactlist/ContactListViewModel.kt +++ b/atox/src/main/kotlin/ui/contactlist/ContactListViewModel.kt @@ -116,5 +116,5 @@ class ContactListViewModel @Inject constructor( } } - fun onShareText(what: String, to: Contact) = chatManager.sendMessage(PublicKey(to.publicKey), what) + fun onShareText(what: String, to: Contact) = chatManager.sendMessage(to.publicKey, what) } diff --git a/atox/src/main/kotlin/ui/contactprofile/ContactProfileFragment.kt b/atox/src/main/kotlin/ui/contactprofile/ContactProfileFragment.kt index 0c65e2815..972d7e7a9 100644 --- a/atox/src/main/kotlin/ui/contactprofile/ContactProfileFragment.kt +++ b/atox/src/main/kotlin/ui/contactprofile/ContactProfileFragment.kt @@ -43,7 +43,7 @@ class ContactProfileFragment : BaseFragment(Fragm headerMainText.text = contact.name avatarImageView.setFrom(contact) - contactPublicKey.text = contact.publicKey + contactPublicKey.text = contact.publicKey.string() contactName.text = contact.name contactStatusMessage.text = contact.statusMessage contactConnectionStatus.text = when (contact.connectionStatus) { diff --git a/atox/src/main/kotlin/ui/createprofile/CreateProfileFragment.kt b/atox/src/main/kotlin/ui/createprofile/CreateProfileFragment.kt index cf243c87d..e66d4e8c7 100644 --- a/atox/src/main/kotlin/ui/createprofile/CreateProfileFragment.kt +++ b/atox/src/main/kotlin/ui/createprofile/CreateProfileFragment.kt @@ -1,4 +1,5 @@ -// SPDX-FileCopyrightText: 2020-2021 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén +// SPDX-FileCopyrightText: 2022 aTox contributors // // SPDX-License-Identifier: GPL-3.0-only @@ -91,7 +92,7 @@ class CreateProfileFragment : BaseFragment(FragmentProfi viewModel.startTox() val user = User( - publicKey = viewModel.publicKey.string(), + publicKey = viewModel.publicKey, name = if (username.text.isNotEmpty()) username.text.toString() else getString(R.string.name_default), statusMessage = getString(R.string.status_message_default), ) diff --git a/atox/src/main/kotlin/ui/friendrequest/FriendRequestFragment.kt b/atox/src/main/kotlin/ui/friendrequest/FriendRequestFragment.kt index 22bcce77f..687527c53 100644 --- a/atox/src/main/kotlin/ui/friendrequest/FriendRequestFragment.kt +++ b/atox/src/main/kotlin/ui/friendrequest/FriendRequestFragment.kt @@ -41,7 +41,7 @@ class FriendRequestFragment : BaseFragment(Fragmen vm.byId(PublicKey(requireStringArg(FRIEND_REQUEST_PUBLIC_KEY))).observe(viewLifecycleOwner) { friendRequest = it - from.text = it.publicKey + from.text = it.publicKey.string() message.text = it.message reject.isEnabled = true accept.isEnabled = true diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 154abd50c..9be0fc1e6 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -15,6 +15,7 @@ android { minSdk = libs.versions.sdk.min.get().toInt() testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" ksp { + arg("room.generateKotlin", "true") arg("room.schemaLocation", "$projectDir/schemas") } } diff --git a/core/src/androidTest/kotlin/db/ContactDaoTest.kt b/core/src/androidTest/kotlin/db/ContactDaoTest.kt index 381d3ee52..5ae2ed2c4 100644 --- a/core/src/androidTest/kotlin/db/ContactDaoTest.kt +++ b/core/src/androidTest/kotlin/db/ContactDaoTest.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020-2022 aTox contributors +// SPDX-FileCopyrightText: 2020-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -18,6 +18,7 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import ltd.evilcorp.core.vo.ConnectionStatus import ltd.evilcorp.core.vo.Contact +import ltd.evilcorp.core.vo.PublicKey import ltd.evilcorp.core.vo.UserStatus import org.junit.runner.RunWith @@ -31,7 +32,7 @@ class ContactDaoTest { private val dao = db.contactDao() private val first = Contact( - publicKey = "1234", + publicKey = PublicKey("1234"), name = "name", statusMessage = "status", lastMessage = 5, @@ -43,7 +44,7 @@ class ContactDaoTest { draftMessage = "i made this", ) - private val second = first.copy(publicKey = "5678") + private val second = first.copy(publicKey = PublicKey("5678")) @BeforeTest fun clearDb() { diff --git a/core/src/androidTest/kotlin/db/DatabaseMigrationTest.kt b/core/src/androidTest/kotlin/db/DatabaseMigrationTest.kt index 163d27408..f7e48d4d2 100644 --- a/core/src/androidTest/kotlin/db/DatabaseMigrationTest.kt +++ b/core/src/androidTest/kotlin/db/DatabaseMigrationTest.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -17,6 +17,7 @@ import ltd.evilcorp.core.vo.FileKind import ltd.evilcorp.core.vo.FileTransfer import ltd.evilcorp.core.vo.Message import ltd.evilcorp.core.vo.MessageType +import ltd.evilcorp.core.vo.PublicKey import ltd.evilcorp.core.vo.Sender import ltd.evilcorp.core.vo.UserStatus import org.junit.Rule @@ -37,7 +38,7 @@ class DatabaseMigrationTest { ) private val ft = FileTransfer( - "76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39", + PublicKey("76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39"), 123, FileKind.Avatar.ordinal, 9876, @@ -47,7 +48,7 @@ class DatabaseMigrationTest { ) private val msg = Message( - "76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39", + PublicKey("76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39"), "hello i am robot beep beep boop", Sender.Sent, MessageType.Normal, @@ -56,7 +57,7 @@ class DatabaseMigrationTest { ) private val contact = Contact( - "76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39", + PublicKey("76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39"), "robinli", "Hello I am robot beep beep boop", 100, @@ -87,7 +88,7 @@ class DatabaseMigrationTest { val cursor = db.query("SELECT * FROM contacts").apply { moveToFirst() } assertEquals(cursor.columnCount, 8) with(contact) { - assertEquals(publicKey, cursor.getString(0)) + assertEquals(publicKey.string(), cursor.getString(0)) assertEquals(name, cursor.getString(1)) assertEquals(statusMessage, cursor.getString(2)) assertEquals(lastMessage, cursor.getLong(3)) @@ -102,7 +103,7 @@ class DatabaseMigrationTest { val cursor = db.query("SELECT * FROM contacts").apply { moveToFirst() } assertEquals(cursor.columnCount, 9) with(contact) { - assertEquals(publicKey, cursor.getString(0)) + assertEquals(publicKey.string(), cursor.getString(0)) assertEquals(name, cursor.getString(1)) assertEquals(statusMessage, cursor.getString(2)) assertEquals(lastMessage, cursor.getLong(3)) @@ -136,7 +137,7 @@ class DatabaseMigrationTest { with(msg) { cursor.moveToFirst() assertEquals(id, cursor.getLong(0)) - assertEquals(publicKey, cursor.getString(1)) + assertEquals(publicKey.string(), cursor.getString(1)) assertEquals(message, cursor.getString(2)) assertEquals(sender.ordinal, cursor.getInt(3)) assertEquals(correlationId, cursor.getInt(4)) @@ -151,7 +152,7 @@ class DatabaseMigrationTest { with(msg) { cursor.moveToFirst() assertEquals(id, cursor.getLong(0)) - assertEquals(publicKey, cursor.getString(1)) + assertEquals(publicKey.string(), cursor.getString(1)) assertEquals(message, cursor.getString(2)) assertEquals(sender.ordinal, cursor.getInt(3)) assertEquals(correlationId, cursor.getInt(4)) @@ -183,7 +184,7 @@ class DatabaseMigrationTest { assertEquals(7, cursor.columnCount) with(ft) { cursor.moveToFirst() - assertEquals(publicKey, cursor.getString(0)) + assertEquals(publicKey.string(), cursor.getString(0)) assertEquals(fileNumber, cursor.getInt(1)) assertEquals(fileKind, cursor.getInt(2)) assertEquals(fileSize, cursor.getLong(3)) @@ -222,7 +223,7 @@ class DatabaseMigrationTest { val cursor = db.query("SELECT * FROM contacts").apply { moveToFirst() } assertEquals(9, cursor.columnCount) with(contact) { - assertEquals(publicKey, cursor.getString(0)) + assertEquals(publicKey.string(), cursor.getString(0)) assertEquals(name, cursor.getString(1)) assertEquals(statusMessage, cursor.getString(2)) assertEquals(lastMessage, cursor.getLong(3)) @@ -238,7 +239,7 @@ class DatabaseMigrationTest { val cursor = db.query("SELECT * FROM contacts").apply { moveToFirst() } assertEquals(10, cursor.columnCount) with(contact) { - assertEquals(publicKey, cursor.getString(0)) + assertEquals(publicKey.string(), cursor.getString(0)) assertEquals(name, cursor.getString(1)) assertEquals(statusMessage, cursor.getString(2)) assertEquals(lastMessage, cursor.getLong(3)) @@ -299,7 +300,7 @@ class DatabaseMigrationTest { assertEquals(cursor.columnCount, 8) with(contact) { cursor.moveToFirst() - assertEquals(publicKey, cursor.getString(0)) + assertEquals(publicKey.string(), cursor.getString(0)) assertEquals(name, cursor.getString(1)) assertEquals(statusMessage, cursor.getString(2)) assertEquals(lastMessage, cursor.getLong(3)) @@ -314,7 +315,7 @@ class DatabaseMigrationTest { with(msg) { cursor.moveToFirst() assertEquals(id, cursor.getLong(0)) - assertEquals(publicKey, cursor.getString(1)) + assertEquals(publicKey.string(), cursor.getString(1)) assertEquals(message, cursor.getString(2)) assertEquals(sender.ordinal, cursor.getInt(3)) assertEquals(correlationId, cursor.getInt(4)) @@ -325,7 +326,7 @@ class DatabaseMigrationTest { assertEquals(7, cursor.columnCount) with(ft) { cursor.moveToFirst() - assertEquals(publicKey, cursor.getString(0)) + assertEquals(publicKey.string(), cursor.getString(0)) assertEquals(fileNumber, cursor.getInt(1)) assertEquals(fileKind, cursor.getInt(2)) assertEquals(fileSize, cursor.getLong(3)) @@ -341,7 +342,7 @@ class DatabaseMigrationTest { assertEquals(cursor.columnCount, 10) with(contact) { cursor.moveToFirst() - assertEquals(publicKey, cursor.getString(0)) + assertEquals(publicKey.string(), cursor.getString(0)) assertEquals(name, cursor.getString(1)) assertEquals(statusMessage, cursor.getString(2)) assertEquals(lastMessage, cursor.getLong(3)) @@ -358,7 +359,7 @@ class DatabaseMigrationTest { with(msg) { cursor.moveToFirst() assertEquals(id, cursor.getLong(0)) - assertEquals(publicKey, cursor.getString(1)) + assertEquals(publicKey.string(), cursor.getString(1)) assertEquals(message, cursor.getString(2)) assertEquals(sender.ordinal, cursor.getInt(3)) assertEquals(correlationId, cursor.getInt(4)) diff --git a/core/src/androidTest/kotlin/db/UserDaoTest.kt b/core/src/androidTest/kotlin/db/UserDaoTest.kt index 82d2ef7c4..5df59d069 100644 --- a/core/src/androidTest/kotlin/db/UserDaoTest.kt +++ b/core/src/androidTest/kotlin/db/UserDaoTest.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021-2022 aTox contributors +// SPDX-FileCopyrightText: 2020-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -17,6 +17,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import ltd.evilcorp.core.vo.ConnectionStatus +import ltd.evilcorp.core.vo.PublicKey import ltd.evilcorp.core.vo.User import ltd.evilcorp.core.vo.UserStatus import org.junit.Test @@ -32,7 +33,7 @@ class UserDaoTest { private val dao = db.userDao() private val first = User( - publicKey = "1234", + publicKey = PublicKey("1234"), name = "name", statusMessage = "status", status = UserStatus.Away, diff --git a/core/src/main/kotlin/db/ContactDao.kt b/core/src/main/kotlin/db/ContactDao.kt index b297241fd..1aea2e775 100644 --- a/core/src/main/kotlin/db/ContactDao.kt +++ b/core/src/main/kotlin/db/ContactDao.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2020 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -13,6 +13,7 @@ import androidx.room.Update import kotlinx.coroutines.flow.Flow import ltd.evilcorp.core.vo.ConnectionStatus import ltd.evilcorp.core.vo.Contact +import ltd.evilcorp.core.vo.PublicKey import ltd.evilcorp.core.vo.UserStatus @Dao @@ -26,11 +27,11 @@ interface ContactDao { @Delete fun delete(contact: Contact) - @Query("SELECT COUNT(*) FROM contacts WHERE public_key = :publicKey") - fun exists(publicKey: String): Boolean + @Query("SELECT COUNT(*) FROM contacts WHERE public_key = :pk") + fun exists(pk: PublicKey): Boolean - @Query("SELECT * FROM contacts WHERE public_key = :publicKey") - fun load(publicKey: String): Flow + @Query("SELECT * FROM contacts WHERE public_key = :pk") + fun load(pk: PublicKey): Flow @Query("SELECT * FROM contacts") fun loadAll(): Flow> @@ -38,30 +39,30 @@ interface ContactDao { @Query("UPDATE contacts SET connection_status = :status, typing = :typing") fun resetTransientData(status: ConnectionStatus = ConnectionStatus.None, typing: Boolean = false) - @Query("UPDATE contacts SET name = :name WHERE public_key = :publicKey") - fun setName(publicKey: String, name: String) + @Query("UPDATE contacts SET name = :name WHERE public_key = :pk") + fun setName(pk: PublicKey, name: String) - @Query("UPDATE contacts SET status_message = :statusMessage WHERE public_key = :publicKey") - fun setStatusMessage(publicKey: String, statusMessage: String) + @Query("UPDATE contacts SET status_message = :statusMessage WHERE public_key = :pk") + fun setStatusMessage(pk: PublicKey, statusMessage: String) - @Query("UPDATE contacts SET last_message = :lastMessage WHERE public_key = :publicKey") - fun setLastMessage(publicKey: String, lastMessage: Long) + @Query("UPDATE contacts SET last_message = :lastMessage WHERE public_key = :pk") + fun setLastMessage(pk: PublicKey, lastMessage: Long) - @Query("UPDATE contacts SET status = :status WHERE public_key = :publicKey") - fun setUserStatus(publicKey: String, status: UserStatus) + @Query("UPDATE contacts SET status = :status WHERE public_key = :pk") + fun setUserStatus(pk: PublicKey, status: UserStatus) - @Query("UPDATE contacts SET connection_status = :connectionStatus WHERE public_key = :publicKey") - fun setConnectionStatus(publicKey: String, connectionStatus: ConnectionStatus) + @Query("UPDATE contacts SET connection_status = :connectionStatus WHERE public_key = :pk") + fun setConnectionStatus(pk: PublicKey, connectionStatus: ConnectionStatus) - @Query("UPDATE contacts SET typing = :typing WHERE public_key = :publicKey") - fun setTyping(publicKey: String, typing: Boolean) + @Query("UPDATE contacts SET typing = :typing WHERE public_key = :pk") + fun setTyping(pk: PublicKey, typing: Boolean) - @Query("UPDATE contacts SET avatar_uri = :uri WHERE public_key = :publicKey") - fun setAvatarUri(publicKey: String, uri: String) + @Query("UPDATE contacts SET avatar_uri = :uri WHERE public_key = :pk") + fun setAvatarUri(pk: PublicKey, uri: String) - @Query("UPDATE contacts SET has_unread_messages = :anyUnread WHERE public_key = :publicKey") - fun setHasUnreadMessages(publicKey: String, anyUnread: Boolean) + @Query("UPDATE contacts SET has_unread_messages = :anyUnread WHERE public_key = :pk") + fun setHasUnreadMessages(pk: PublicKey, anyUnread: Boolean) - @Query("UPDATE contacts SET draft_message = :draft WHERE public_key = :publicKey") - fun setDraftMessage(publicKey: String, draft: String) + @Query("UPDATE contacts SET draft_message = :draft WHERE public_key = :pk") + fun setDraftMessage(pk: PublicKey, draft: String) } diff --git a/core/src/main/kotlin/db/Converters.kt b/core/src/main/kotlin/db/Converters.kt index d880671fe..00ab4dd0c 100644 --- a/core/src/main/kotlin/db/Converters.kt +++ b/core/src/main/kotlin/db/Converters.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2024 Robin Lindén +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -7,6 +7,7 @@ package ltd.evilcorp.core.db import androidx.room.TypeConverter import ltd.evilcorp.core.vo.ConnectionStatus import ltd.evilcorp.core.vo.MessageType +import ltd.evilcorp.core.vo.PublicKey import ltd.evilcorp.core.vo.Sender import ltd.evilcorp.core.vo.UserStatus @@ -43,5 +44,13 @@ class Converters private constructor() { @TypeConverter @JvmStatic fun fromMessageType(type: MessageType): Int = type.ordinal + + @TypeConverter + @JvmStatic + fun toPublicKey(pk: String) = PublicKey(pk) + + @TypeConverter + @JvmStatic + fun fromPublicKey(pk: PublicKey) = pk.string() } } diff --git a/core/src/main/kotlin/db/FileTransferDao.kt b/core/src/main/kotlin/db/FileTransferDao.kt index 7c49ef514..7d274d4fa 100644 --- a/core/src/main/kotlin/db/FileTransferDao.kt +++ b/core/src/main/kotlin/db/FileTransferDao.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2020 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -11,6 +11,7 @@ import androidx.room.Query import kotlinx.coroutines.flow.Flow import ltd.evilcorp.core.vo.FT_REJECTED import ltd.evilcorp.core.vo.FileTransfer +import ltd.evilcorp.core.vo.PublicKey @Dao interface FileTransferDao { @@ -20,8 +21,8 @@ interface FileTransferDao { @Query("DELETE FROM file_transfers WHERE id == :id") fun delete(id: Int) - @Query("SELECT * FROM file_transfers WHERE public_key == :publicKey") - fun load(publicKey: String): Flow> + @Query("SELECT * FROM file_transfers WHERE public_key == :pk") + fun load(pk: PublicKey): Flow> @Query("SELECT * FROM file_transfers WHERE id == :id") fun load(id: Int): Flow diff --git a/core/src/main/kotlin/db/FriendRequestDao.kt b/core/src/main/kotlin/db/FriendRequestDao.kt index f8bd5c319..a0ce21414 100644 --- a/core/src/main/kotlin/db/FriendRequestDao.kt +++ b/core/src/main/kotlin/db/FriendRequestDao.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2024 Robin Lindén +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -11,6 +11,7 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import kotlinx.coroutines.flow.Flow import ltd.evilcorp.core.vo.FriendRequest +import ltd.evilcorp.core.vo.PublicKey @Dao interface FriendRequestDao { @@ -23,8 +24,8 @@ interface FriendRequestDao { @Query("SELECT * FROM friend_requests") fun loadAll(): Flow> - @Query("SELECT * FROM friend_requests WHERE public_key == :publicKey") - fun load(publicKey: String): Flow + @Query("SELECT * FROM friend_requests WHERE public_key == :pk") + fun load(pk: PublicKey): Flow @Query("SELECT COUNT(public_key) FROM friend_requests") fun count(): Int diff --git a/core/src/main/kotlin/db/MessageDao.kt b/core/src/main/kotlin/db/MessageDao.kt index 12546a5eb..f0fe7e9da 100644 --- a/core/src/main/kotlin/db/MessageDao.kt +++ b/core/src/main/kotlin/db/MessageDao.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2021 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -10,6 +10,7 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import kotlinx.coroutines.flow.Flow import ltd.evilcorp.core.vo.Message +import ltd.evilcorp.core.vo.PublicKey @Dao interface MessageDao { @@ -17,22 +18,22 @@ interface MessageDao { fun save(message: Message) @Query("SELECT * FROM messages WHERE conversation == :conversation") - fun load(conversation: String): Flow> + fun load(conversation: PublicKey): Flow> @Query("SELECT * FROM messages WHERE conversation == :conversation AND timestamp == 0") - fun loadPending(conversation: String): List + fun loadPending(conversation: PublicKey): List @Query("UPDATE messages SET correlation_id = :correlationId WHERE id == :id") fun setCorrelationId(id: Long, correlationId: Int) @Query("DELETE FROM messages WHERE conversation == :conversation") - fun delete(conversation: String) + fun delete(conversation: PublicKey) @Suppress("ktlint:standard:max-line-length") @Query( "UPDATE messages SET timestamp = :timestamp WHERE conversation == :conversation AND correlation_id == :correlationId AND timestamp == 0", ) - fun setReceipt(conversation: String, correlationId: Int, timestamp: Long) + fun setReceipt(conversation: PublicKey, correlationId: Int, timestamp: Long) @Query("DELETE FROM messages WHERE id = :id") fun deleteMessage(id: Long) diff --git a/core/src/main/kotlin/db/UserDao.kt b/core/src/main/kotlin/db/UserDao.kt index 75d3d186e..1045add99 100644 --- a/core/src/main/kotlin/db/UserDao.kt +++ b/core/src/main/kotlin/db/UserDao.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2021 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -11,6 +11,7 @@ import androidx.room.Query import androidx.room.Update import kotlinx.coroutines.flow.Flow import ltd.evilcorp.core.vo.ConnectionStatus +import ltd.evilcorp.core.vo.PublicKey import ltd.evilcorp.core.vo.User import ltd.evilcorp.core.vo.UserStatus @@ -22,21 +23,21 @@ interface UserDao { @Update fun update(user: User) - @Query("UPDATE users SET name = :name WHERE public_key == :publicKey") - fun updateName(publicKey: String, name: String) + @Query("UPDATE users SET name = :name WHERE public_key == :pk") + fun updateName(pk: PublicKey, name: String) - @Query("UPDATE users SET status_message = :statusMessage WHERE public_key == :publicKey") - fun updateStatusMessage(publicKey: String, statusMessage: String) + @Query("UPDATE users SET status_message = :statusMessage WHERE public_key == :pk") + fun updateStatusMessage(pk: PublicKey, statusMessage: String) - @Query("UPDATE users SET connection_status = :connectionStatus WHERE public_key == :publicKey") - fun updateConnection(publicKey: String, connectionStatus: ConnectionStatus) + @Query("UPDATE users SET connection_status = :connectionStatus WHERE public_key == :pk") + fun updateConnection(pk: PublicKey, connectionStatus: ConnectionStatus) - @Query("UPDATE users SET status = :status WHERE public_key == :publicKey") - fun updateStatus(publicKey: String, status: UserStatus) + @Query("UPDATE users SET status = :status WHERE public_key == :pk") + fun updateStatus(pk: PublicKey, status: UserStatus) - @Query("SELECT COUNT(*) FROM users WHERE public_key = :publicKey") - fun exists(publicKey: String): Boolean + @Query("SELECT COUNT(*) FROM users WHERE public_key = :pk") + fun exists(pk: PublicKey): Boolean - @Query("SELECT * FROM users WHERE public_key = :publicKey") - fun load(publicKey: String): Flow + @Query("SELECT * FROM users WHERE public_key = :pk") + fun load(pk: PublicKey): Flow } diff --git a/core/src/main/kotlin/repository/ContactRepository.kt b/core/src/main/kotlin/repository/ContactRepository.kt index 15525589a..bb33a625e 100644 --- a/core/src/main/kotlin/repository/ContactRepository.kt +++ b/core/src/main/kotlin/repository/ContactRepository.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2020 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -10,25 +10,26 @@ import kotlinx.coroutines.flow.Flow import ltd.evilcorp.core.db.ContactDao import ltd.evilcorp.core.vo.ConnectionStatus import ltd.evilcorp.core.vo.Contact +import ltd.evilcorp.core.vo.PublicKey import ltd.evilcorp.core.vo.UserStatus @Singleton class ContactRepository @Inject constructor(private val dao: ContactDao) { - fun exists(publicKey: String): Boolean = dao.exists(publicKey) + fun exists(publicKey: PublicKey): Boolean = dao.exists(publicKey) fun add(contact: Contact) = dao.save(contact) fun update(contact: Contact) = dao.update(contact) fun delete(contact: Contact) = dao.delete(contact) - fun get(publicKey: String): Flow = dao.load(publicKey) + fun get(publicKey: PublicKey): Flow = dao.load(publicKey) fun getAll(): Flow> = dao.loadAll() fun resetTransientData() = dao.resetTransientData() - fun setName(publicKey: String, name: String) = dao.setName(publicKey, name) - fun setStatusMessage(publicKey: String, statusMessage: String) = dao.setStatusMessage(publicKey, statusMessage) - fun setLastMessage(publicKey: String, lastMessage: Long) = dao.setLastMessage(publicKey, lastMessage) - fun setUserStatus(publicKey: String, status: UserStatus) = dao.setUserStatus(publicKey, status) - fun setConnectionStatus(publicKey: String, status: ConnectionStatus) = dao.setConnectionStatus(publicKey, status) - fun setTyping(publicKey: String, typing: Boolean) = dao.setTyping(publicKey, typing) - fun setAvatarUri(publicKey: String, uri: String) = dao.setAvatarUri(publicKey, uri) - fun setHasUnreadMessages(publicKey: String, anyUnread: Boolean) = dao.setHasUnreadMessages(publicKey, anyUnread) - fun setDraftMessage(publicKey: String, draft: String) = dao.setDraftMessage(publicKey, draft) + fun setName(pk: PublicKey, name: String) = dao.setName(pk, name) + fun setStatusMessage(pk: PublicKey, statusMessage: String) = dao.setStatusMessage(pk, statusMessage) + fun setLastMessage(pk: PublicKey, lastMessage: Long) = dao.setLastMessage(pk, lastMessage) + fun setUserStatus(pk: PublicKey, status: UserStatus) = dao.setUserStatus(pk, status) + fun setConnectionStatus(pk: PublicKey, status: ConnectionStatus) = dao.setConnectionStatus(pk, status) + fun setTyping(pk: PublicKey, typing: Boolean) = dao.setTyping(pk, typing) + fun setAvatarUri(pk: PublicKey, uri: String) = dao.setAvatarUri(pk, uri) + fun setHasUnreadMessages(pk: PublicKey, anyUnread: Boolean) = dao.setHasUnreadMessages(pk, anyUnread) + fun setDraftMessage(pk: PublicKey, draft: String) = dao.setDraftMessage(pk, draft) } diff --git a/core/src/main/kotlin/repository/FileTransferRepository.kt b/core/src/main/kotlin/repository/FileTransferRepository.kt index dfbfe2131..cab58200a 100644 --- a/core/src/main/kotlin/repository/FileTransferRepository.kt +++ b/core/src/main/kotlin/repository/FileTransferRepository.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2020 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -9,6 +9,7 @@ import javax.inject.Singleton import kotlinx.coroutines.flow.Flow import ltd.evilcorp.core.db.FileTransferDao import ltd.evilcorp.core.vo.FileTransfer +import ltd.evilcorp.core.vo.PublicKey @Singleton class FileTransferRepository @Inject internal constructor(private val dao: FileTransferDao) { @@ -16,7 +17,7 @@ class FileTransferRepository @Inject internal constructor(private val dao: FileT fun delete(id: Int) = dao.delete(id) - fun get(publicKey: String): Flow> = dao.load(publicKey) + fun get(pk: PublicKey): Flow> = dao.load(pk) fun get(id: Int): Flow = dao.load(id) diff --git a/core/src/main/kotlin/repository/FriendRequestRepository.kt b/core/src/main/kotlin/repository/FriendRequestRepository.kt index a3d44ade7..2b88dbe0b 100644 --- a/core/src/main/kotlin/repository/FriendRequestRepository.kt +++ b/core/src/main/kotlin/repository/FriendRequestRepository.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2024 Robin Lindén +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -9,6 +9,7 @@ import javax.inject.Singleton import kotlinx.coroutines.flow.Flow import ltd.evilcorp.core.db.FriendRequestDao import ltd.evilcorp.core.vo.FriendRequest +import ltd.evilcorp.core.vo.PublicKey @Singleton class FriendRequestRepository @Inject internal constructor(private val friendRequestDao: FriendRequestDao) { @@ -18,7 +19,7 @@ class FriendRequestRepository @Inject internal constructor(private val friendReq fun getAll(): Flow> = friendRequestDao.loadAll() - fun get(publicKey: String): Flow = friendRequestDao.load(publicKey) + fun get(pk: PublicKey): Flow = friendRequestDao.load(pk) fun count(): Int = friendRequestDao.count() } diff --git a/core/src/main/kotlin/repository/MessageRepository.kt b/core/src/main/kotlin/repository/MessageRepository.kt index 920db0f86..b9e4271f6 100644 --- a/core/src/main/kotlin/repository/MessageRepository.kt +++ b/core/src/main/kotlin/repository/MessageRepository.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2020 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -10,6 +10,7 @@ import javax.inject.Singleton import kotlinx.coroutines.flow.Flow import ltd.evilcorp.core.db.MessageDao import ltd.evilcorp.core.vo.Message +import ltd.evilcorp.core.vo.PublicKey @Singleton class MessageRepository @Inject internal constructor( @@ -21,16 +22,16 @@ class MessageRepository @Inject internal constructor( contactRepository.setLastMessage(message.publicKey, Date().time) } - fun get(conversation: String): Flow> = messageDao.load(conversation) + fun get(conversation: PublicKey): Flow> = messageDao.load(conversation) - fun getPending(conversation: String): List = messageDao.loadPending(conversation) + fun getPending(conversation: PublicKey): List = messageDao.loadPending(conversation) fun setCorrelationId(id: Long, correlationId: Int) = messageDao.setCorrelationId(id, correlationId) - fun delete(conversation: String) = messageDao.delete(conversation) + fun delete(conversation: PublicKey) = messageDao.delete(conversation) fun deleteMessage(id: Long) = messageDao.deleteMessage(id) - fun setReceipt(conversation: String, correlationId: Int, timestamp: Long) = + fun setReceipt(conversation: PublicKey, correlationId: Int, timestamp: Long) = messageDao.setReceipt(conversation, correlationId, timestamp) } diff --git a/core/src/main/kotlin/repository/UserRepository.kt b/core/src/main/kotlin/repository/UserRepository.kt index 595fa68f7..bd7925094 100644 --- a/core/src/main/kotlin/repository/UserRepository.kt +++ b/core/src/main/kotlin/repository/UserRepository.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2020 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -9,26 +9,26 @@ import javax.inject.Singleton import kotlinx.coroutines.flow.Flow import ltd.evilcorp.core.db.UserDao import ltd.evilcorp.core.vo.ConnectionStatus +import ltd.evilcorp.core.vo.PublicKey import ltd.evilcorp.core.vo.User import ltd.evilcorp.core.vo.UserStatus @Singleton class UserRepository @Inject constructor(private val userDao: UserDao) { - fun exists(publicKey: String): Boolean = userDao.exists(publicKey) + fun exists(pk: PublicKey): Boolean = userDao.exists(pk) fun add(user: User) = userDao.save(user) fun update(user: User) = userDao.update(user) - fun get(publicKey: String): Flow = userDao.load(publicKey) + fun get(pk: PublicKey): Flow = userDao.load(pk) - fun updateName(publicKey: String, name: String) = userDao.updateName(publicKey, name) + fun updateName(pk: PublicKey, name: String) = userDao.updateName(pk, name) - fun updateStatusMessage(publicKey: String, statusMessage: String) = - userDao.updateStatusMessage(publicKey, statusMessage) + fun updateStatusMessage(pk: PublicKey, statusMessage: String) = userDao.updateStatusMessage(pk, statusMessage) - fun updateConnection(publicKey: String, connectionStatus: ConnectionStatus) = - userDao.updateConnection(publicKey, connectionStatus) + fun updateConnection(pk: PublicKey, connectionStatus: ConnectionStatus) = + userDao.updateConnection(pk, connectionStatus) - fun updateStatus(publicKey: String, status: UserStatus) = userDao.updateStatus(publicKey, status) + fun updateStatus(pk: PublicKey, status: UserStatus) = userDao.updateStatus(pk, status) } diff --git a/core/src/main/kotlin/vo/Contact.kt b/core/src/main/kotlin/vo/Contact.kt index 2a0c0e59a..f490d6db7 100644 --- a/core/src/main/kotlin/vo/Contact.kt +++ b/core/src/main/kotlin/vo/Contact.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2021 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -25,7 +25,7 @@ enum class UserStatus { data class Contact( @PrimaryKey @ColumnInfo(name = "public_key") - val publicKey: String, + val publicKey: PublicKey, @ColumnInfo(name = "name") var name: String = "", diff --git a/core/src/main/kotlin/vo/FileTransfer.kt b/core/src/main/kotlin/vo/FileTransfer.kt index 1fac145dc..7466efa20 100644 --- a/core/src/main/kotlin/vo/FileTransfer.kt +++ b/core/src/main/kotlin/vo/FileTransfer.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2020 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -21,7 +21,7 @@ const val FT_REJECTED = -2L @Entity(tableName = "file_transfers") data class FileTransfer( @ColumnInfo(name = "public_key") - val publicKey: String, + val publicKey: PublicKey, @ColumnInfo(name = "file_number") val fileNumber: Int, diff --git a/core/src/main/kotlin/vo/FriendRequest.kt b/core/src/main/kotlin/vo/FriendRequest.kt index 593565b95..6f7eb08a7 100644 --- a/core/src/main/kotlin/vo/FriendRequest.kt +++ b/core/src/main/kotlin/vo/FriendRequest.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -12,7 +12,7 @@ import androidx.room.PrimaryKey data class FriendRequest( @PrimaryKey @ColumnInfo(name = "public_key") - val publicKey: String, + val publicKey: PublicKey, @ColumnInfo(name = "message") val message: String = "", diff --git a/core/src/main/kotlin/vo/Message.kt b/core/src/main/kotlin/vo/Message.kt index 03e3d60b4..50c5564d2 100644 --- a/core/src/main/kotlin/vo/Message.kt +++ b/core/src/main/kotlin/vo/Message.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2020 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -22,7 +22,7 @@ enum class MessageType { @Entity(tableName = "messages") data class Message( @ColumnInfo(name = "conversation") - val publicKey: String, + val publicKey: PublicKey, @ColumnInfo(name = "message") val message: String, diff --git a/core/src/main/kotlin/vo/User.kt b/core/src/main/kotlin/vo/User.kt index abfaa7dba..c5774c8bb 100644 --- a/core/src/main/kotlin/vo/User.kt +++ b/core/src/main/kotlin/vo/User.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019 aTox contributors +// SPDX-FileCopyrightText: 2019-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -12,7 +12,7 @@ import androidx.room.PrimaryKey data class User( @PrimaryKey @ColumnInfo(name = "public_key") - val publicKey: String, + val publicKey: PublicKey, @ColumnInfo(name = "name") var name: String = "aTox user", diff --git a/domain/src/main/kotlin/feature/CallManager.kt b/domain/src/main/kotlin/feature/CallManager.kt index 7a8240991..683f28d26 100644 --- a/domain/src/main/kotlin/feature/CallManager.kt +++ b/domain/src/main/kotlin/feature/CallManager.kt @@ -50,14 +50,14 @@ class CallManager @Inject constructor(private val tox: Tox, private val scope: C val calls = mutableSetOf().apply { addAll(_pendingCalls.value) } calls.addAll(_pendingCalls.value) if (calls.add(from)) { - Log.i(TAG, "Added pending call ${from.publicKey.take(8)}") + Log.i(TAG, "Added pending call ${from.publicKey.fingerprint()}") _pendingCalls.value = calls } } fun removePendingCall(pk: PublicKey) { val calls = mutableSetOf().apply { addAll(_pendingCalls.value) } - val removed = calls.firstOrNull { it.publicKey == pk.string() } + val removed = calls.firstOrNull { it.publicKey == pk } if (removed != null) { Log.i(TAG, "Removed pending call ${pk.fingerprint()}") calls.remove(removed) @@ -66,7 +66,7 @@ class CallManager @Inject constructor(private val tox: Tox, private val scope: C } fun startCall(publicKey: PublicKey) { - if (pendingCalls.value.any { it.publicKey == publicKey.string() }) { + if (pendingCalls.value.any { it.publicKey == publicKey }) { tox.answerCall(publicKey) } else { tox.startCall(publicKey) diff --git a/domain/src/main/kotlin/feature/ChatManager.kt b/domain/src/main/kotlin/feature/ChatManager.kt index 2b1a4ee95..eac5ad885 100644 --- a/domain/src/main/kotlin/feature/ChatManager.kt +++ b/domain/src/main/kotlin/feature/ChatManager.kt @@ -47,20 +47,20 @@ class ChatManager @Inject constructor( private val messageRepository: MessageRepository, private val tox: Tox, ) { - var activeChat = "" + var activeChat = PublicKey("") set(value) { field = value - if (value.isNotEmpty()) { + if (value.string().isNotEmpty()) { scope.launch { contactRepository.setHasUnreadMessages(value, false) } } } - fun messagesFor(publicKey: PublicKey) = messageRepository.get(publicKey.string()) + fun messagesFor(publicKey: PublicKey) = messageRepository.get(publicKey) fun sendMessage(publicKey: PublicKey, message: String, type: MessageType = MessageType.Normal) = scope.launch { - if (contactRepository.get(publicKey.string()).first().connectionStatus == ConnectionStatus.None) { + if (contactRepository.get(publicKey).first().connectionStatus == ConnectionStatus.None) { queueMessage(publicKey, message, type) return@launch } @@ -72,7 +72,7 @@ class ChatManager @Inject constructor( messageRepository.add( Message( - publicKey.string(), + publicKey, message, Sender.Sent, type, @@ -82,19 +82,19 @@ class ChatManager @Inject constructor( } private fun queueMessage(publicKey: PublicKey, message: String, type: MessageType) = - messageRepository.add(Message(publicKey.string(), message, Sender.Sent, type, Int.MIN_VALUE)) + messageRepository.add(Message(publicKey, message, Sender.Sent, type, Int.MIN_VALUE)) fun resend(messages: List) = scope.launch { for (message in messages) { val msgs = message.message.chunked(MAX_MESSAGE_LENGTH) while (msgs.size > 1) { - tox.sendMessage(PublicKey(message.publicKey), msgs.removeAt(0), message.type) + tox.sendMessage(message.publicKey, msgs.removeAt(0), message.type) } messageRepository.setCorrelationId( message.id, - tox.sendMessage(PublicKey(message.publicKey), msgs.first(), message.type), + tox.sendMessage(message.publicKey, msgs.first(), message.type), ) } } @@ -104,8 +104,8 @@ class ChatManager @Inject constructor( } fun clearHistory(publicKey: PublicKey) = scope.launch { - messageRepository.delete(publicKey.string()) - contactRepository.setLastMessage(publicKey.string(), 0) + messageRepository.delete(publicKey) + contactRepository.setLastMessage(publicKey, 0) } fun setTyping(publicKey: PublicKey, typing: Boolean) = scope.launch { diff --git a/domain/src/main/kotlin/feature/ContactManager.kt b/domain/src/main/kotlin/feature/ContactManager.kt index 525ab3914..b4bcc87bc 100644 --- a/domain/src/main/kotlin/feature/ContactManager.kt +++ b/domain/src/main/kotlin/feature/ContactManager.kt @@ -19,22 +19,22 @@ class ContactManager @Inject constructor( private val contactRepository: ContactRepository, private val tox: Tox, ) { - fun get(publicKey: PublicKey) = contactRepository.get(publicKey.string()) + fun get(publicKey: PublicKey) = contactRepository.get(publicKey) fun getAll() = contactRepository.getAll() fun add(toxID: ToxID, message: String) = scope.launch { - val publicKeyTxt = toxID.toPublicKey().string() tox.addContact(toxID, message) - contactRepository.add(Contact(publicKeyTxt)) - contactRepository.setLastMessage(publicKeyTxt, Date().time) + val pk = toxID.toPublicKey() + contactRepository.add(Contact(pk)) + contactRepository.setLastMessage(pk, Date().time) } fun delete(publicKey: PublicKey) = scope.launch { tox.deleteContact(publicKey) - contactRepository.delete(Contact(publicKey.string())) + contactRepository.delete(Contact(publicKey)) } fun setDraft(pk: PublicKey, draft: String) = scope.launch { - contactRepository.setDraftMessage(pk.string(), draft) + contactRepository.setDraftMessage(pk, draft) } } diff --git a/domain/src/main/kotlin/feature/ExportManager.kt b/domain/src/main/kotlin/feature/ExportManager.kt index 89959a181..a5f3222f3 100644 --- a/domain/src/main/kotlin/feature/ExportManager.kt +++ b/domain/src/main/kotlin/feature/ExportManager.kt @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: 2022 Akito -// SPDX-FileCopyrightText: 2023-2024 Robin Lindén +// SPDX-FileCopyrightText: 2023-2025 Robin Lindén // // SPDX-License-Identifier: GPL-3.0-only @@ -12,18 +12,19 @@ import javax.inject.Inject import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import ltd.evilcorp.core.repository.MessageRepository +import ltd.evilcorp.core.vo.PublicKey import org.json.JSONArray import org.json.JSONObject class ExportManager @Inject constructor(private val messageRepository: MessageRepository) { - fun generateExportMessagesJString(publicKey: String): String { - val messages = runBlocking { messageRepository.get(publicKey).first() } + fun generateExportMessagesJString(pk: PublicKey): String { + val messages = runBlocking { messageRepository.get(pk).first() } val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()) val root = JSONObject() root.put("version", 1) root.put("timestamp", dateFormat.format(Date())) - root.put("contact_public_key", publicKey) + root.put("contact_public_key", pk.string()) val entries = JSONArray() for (message in messages) { diff --git a/domain/src/main/kotlin/feature/FileTransferManager.kt b/domain/src/main/kotlin/feature/FileTransferManager.kt index 2b7892bdd..0e7ef1ada 100644 --- a/domain/src/main/kotlin/feature/FileTransferManager.kt +++ b/domain/src/main/kotlin/feature/FileTransferManager.kt @@ -41,10 +41,6 @@ import ltd.evilcorp.domain.tox.Tox private const val TAG = "FileTransferManager" -// TODO(robinlinden): This will go away when PublicKey is used everywhere it should be. -private const val FINGERPRINT_LEN = 8 -private fun String.fingerprint() = take(FINGERPRINT_LEN) - @Suppress("ArrayInDataClass") private data class Chunk(val pos: Long, val data: ByteArray) @@ -61,7 +57,7 @@ class FileTransferManager @Inject constructor( private val tox: Tox, ) { private val fileTransfers: MutableList = mutableListOf() - private val outgoingFiles = mutableMapOf, OutgoingFile>() + private val outgoingFiles = mutableMapOf, OutgoingFile>() init { File(context.filesDir, "ft").mkdir() @@ -79,7 +75,7 @@ class FileTransferManager @Inject constructor( } } - fun resetForContact(pk: String) { + fun resetForContact(pk: PublicKey) { Log.i(TAG, "Clearing fts for contact ${pk.fingerprint()}") fileTransfers.filter { it.publicKey == pk }.kForEach { ft -> setProgress(ft, FT_REJECTED) @@ -113,7 +109,7 @@ class FileTransferManager @Inject constructor( } else if (ft.fileSize > MAX_AVATAR_SIZE) { Log.e(TAG, "Got trash avatar with size ${ft.fileSize} from ${ft.publicKey}") contactRepository.setAvatarUri(ft.publicKey, "") - tox.stopFileTransfer(PublicKey(ft.publicKey), ft.fileNumber) + tox.stopFileTransfer(ft.publicKey, ft.fileNumber) return -1 } @@ -153,7 +149,7 @@ class FileTransferManager @Inject constructor( RandomAccessFile(file, "rwd").use { it.setLength(ft.fileSize) } setDestination(ft, Uri.fromFile(file)) setProgress(ft, FT_STARTED) - tox.startFileTransfer(PublicKey(ft.publicKey), ft.fileNumber) + tox.startFileTransfer(ft.publicKey, ft.fileNumber) } fun reject(id: Int) { @@ -166,7 +162,7 @@ class FileTransferManager @Inject constructor( Log.i(TAG, "Reject ${ft.fileNumber} for ${ft.publicKey.fingerprint()}") fileTransfers.remove(ft) setProgress(ft, FT_REJECTED) - tox.stopFileTransfer(PublicKey(ft.publicKey), ft.fileNumber) + tox.stopFileTransfer(ft.publicKey, ft.fileNumber) val uri = Uri.parse(ft.destination) if (ft.outgoing) { outgoingFiles.remove(Pair(ft.publicKey, ft.fileNumber))?.inputStream?.close() @@ -191,7 +187,7 @@ class FileTransferManager @Inject constructor( } } - fun addDataToTransfer(publicKey: String, fileNumber: Int, position: Long, data: ByteArray) { + fun addDataToTransfer(publicKey: PublicKey, fileNumber: Int, position: Long, data: ByteArray) { val ft = fileTransfers.find { it.publicKey == publicKey && it.fileNumber == fileNumber } if (ft == null) { if (data.isNotEmpty()) { @@ -223,7 +219,7 @@ class FileTransferManager @Inject constructor( } } - fun transfersFor(publicKey: PublicKey) = fileTransferRepository.get(publicKey.string()) + fun transfersFor(publicKey: PublicKey) = fileTransferRepository.get(publicKey) fun create(pk: PublicKey, file: Uri) { val (name, size) = context.contentResolver.query(file, null, null, null, null, null)?.use { cursor -> @@ -234,7 +230,7 @@ class FileTransferManager @Inject constructor( } ?: return val ft = FileTransfer( - pk.string(), + pk, tox.sendFile(pk, FileKind.Data, size, name), FileKind.Data.ordinal, size, @@ -259,11 +255,11 @@ class FileTransferManager @Inject constructor( // TODO(robinlinden): Handle seek-backs: https://github.com/TokTok/c-toxcore/blob/eeaa039222e7a123c2585c8486ee965017767209/toxcore/tox.h#L2405-L2406 // TODO(robinlinden): An error when sending the last chunk in a transfer will stall it. - fun sendChunk(pk: String, fileNo: Int, pos: Long, length: Int) { + fun sendChunk(pk: PublicKey, fileNo: Int, pos: Long, length: Int) { val ft = fileTransfers.find { it.publicKey == pk && it.fileNumber == fileNo } if (ft == null) { Log.e(TAG, "Received request for chunk of unknown ft ${pk.fingerprint()} $fileNo") - tox.stopFileTransfer(PublicKey(pk), fileNo) + tox.stopFileTransfer(pk, fileNo) return } @@ -280,7 +276,7 @@ class FileTransferManager @Inject constructor( while (file.unsentChunks.isNotEmpty()) { val chunk = file.unsentChunks.first() Log.i(TAG, "Resending chunk @ ${chunk.pos} to ${pk.fingerprint()} ($fileNo)}") - if (tox.sendFileChunk(PublicKey(pk), fileNo, chunk.pos, chunk.data).isFailure) { + if (tox.sendFileChunk(pk, fileNo, chunk.pos, chunk.data).isFailure) { return } setProgress(ft, ft.progress + chunk.data.size) @@ -289,7 +285,7 @@ class FileTransferManager @Inject constructor( val bytes = ByteArray(length) file.inputStream.read(bytes, 0, length) - if (tox.sendFileChunk(PublicKey(pk), fileNo, pos, bytes).isFailure) { + if (tox.sendFileChunk(pk, fileNo, pos, bytes).isFailure) { file.unsentChunks.add(Chunk(pos, bytes)) return } @@ -297,7 +293,7 @@ class FileTransferManager @Inject constructor( setProgress(ft, ft.progress + length) } - fun setStatus(pk: String, fileNo: Int, fileStatus: ToxFileControl) { + fun setStatus(pk: PublicKey, fileNo: Int, fileStatus: ToxFileControl) { Log.e(TAG, "Setting ${pk.fingerprint()} $fileNo to status $fileStatus") val ft = fileTransfers.find { it.publicKey == pk && it.fileNumber == fileNo } if (ft == null) { @@ -314,7 +310,7 @@ class FileTransferManager @Inject constructor( } suspend fun deleteAll(publicKey: PublicKey) { - fileTransferRepository.get(publicKey.string()).take(1).collect { fts -> + fileTransferRepository.get(publicKey).take(1).collect { fts -> fts.kForEach { delete(it.id) } } } diff --git a/domain/src/main/kotlin/feature/FriendRequestManager.kt b/domain/src/main/kotlin/feature/FriendRequestManager.kt index 1ff18a6dd..13bb40149 100644 --- a/domain/src/main/kotlin/feature/FriendRequestManager.kt +++ b/domain/src/main/kotlin/feature/FriendRequestManager.kt @@ -29,11 +29,11 @@ class FriendRequestManager @Inject constructor( private val tox: Tox, ) { fun getAll(): Flow> = friendRequestRepository.getAll() - fun get(id: PublicKey): Flow = friendRequestRepository.get(id.string()) + fun get(id: PublicKey): Flow = friendRequestRepository.get(id) fun accept(friendRequest: FriendRequest) = scope.launch { val acceptTime = Date().time - tox.acceptFriendRequest(PublicKey(friendRequest.publicKey)) + tox.acceptFriendRequest(friendRequest.publicKey) messageRepository.add( Message( friendRequest.publicKey, diff --git a/domain/src/main/kotlin/feature/UserManager.kt b/domain/src/main/kotlin/feature/UserManager.kt index e5564a840..e9ea3df3c 100644 --- a/domain/src/main/kotlin/feature/UserManager.kt +++ b/domain/src/main/kotlin/feature/UserManager.kt @@ -18,7 +18,7 @@ class UserManager @Inject constructor( private val userRepository: UserRepository, private val tox: Tox, ) { - fun get(publicKey: PublicKey) = userRepository.get(publicKey.string()) + fun get(pk: PublicKey) = userRepository.get(pk) fun create(user: User) = scope.launch { userRepository.add(user) @@ -26,27 +26,27 @@ class UserManager @Inject constructor( tox.setStatusMessage(user.statusMessage) } - fun verifyExists(publicKey: PublicKey) = scope.launch { - if (!userRepository.exists(publicKey.string())) { + fun verifyExists(pk: PublicKey) = scope.launch { + if (!userRepository.exists(pk)) { val name = tox.getName() val statusMessage = tox.getStatusMessage() - val user = User(publicKey.string(), name, statusMessage) + val user = User(pk, name, statusMessage) userRepository.add(user) } } fun setName(name: String) = scope.launch { tox.setName(name) - userRepository.updateName(tox.publicKey.string(), name) + userRepository.updateName(tox.publicKey, name) } fun setStatusMessage(statusMessage: String) = scope.launch { tox.setStatusMessage(statusMessage) - userRepository.updateStatusMessage(tox.publicKey.string(), statusMessage) + userRepository.updateStatusMessage(tox.publicKey, statusMessage) } fun setStatus(status: UserStatus) = scope.launch { tox.setStatus(status) - userRepository.updateStatus(tox.publicKey.string(), status) + userRepository.updateStatus(tox.publicKey, status) } } diff --git a/domain/src/main/kotlin/tox/Tox.kt b/domain/src/main/kotlin/tox/Tox.kt index 5dd7ce0e8..1524ecdc1 100644 --- a/domain/src/main/kotlin/tox/Tox.kt +++ b/domain/src/main/kotlin/tox/Tox.kt @@ -89,8 +89,8 @@ class Tox @Inject constructor( contactRepository.resetTransientData() for ((publicKey, _) in tox.getContacts()) { - if (!contactRepository.exists(publicKey.string())) { - contactRepository.add(Contact(publicKey.string())) + if (!contactRepository.exists(publicKey)) { + contactRepository.add(Contact(publicKey)) } } } @@ -106,7 +106,7 @@ class Tox @Inject constructor( fun iterateForever() = scope.launch { running = true - userRepository.updateConnection(publicKey.string(), ConnectionStatus.None) + userRepository.updateConnection(publicKey, ConnectionStatus.None) while (running || toxAvRunning) { if (isBootstrapNeeded) { try { diff --git a/domain/src/main/kotlin/tox/ToxAvEventListener.kt b/domain/src/main/kotlin/tox/ToxAvEventListener.kt index a82454b17..969ed35da 100644 --- a/domain/src/main/kotlin/tox/ToxAvEventListener.kt +++ b/domain/src/main/kotlin/tox/ToxAvEventListener.kt @@ -12,11 +12,11 @@ import ltd.evilcorp.core.vo.PublicKey import scala.Option import scala.Tuple3 -typealias CallHandler = (pk: String, audioEnabled: Boolean, videoEnabled: Boolean) -> Unit -typealias CallStateHandler = (pk: String, callState: EnumSet) -> Unit -typealias VideoBitRateHandler = (pk: String, bitRate: Int) -> Unit +typealias CallHandler = (pk: PublicKey, audioEnabled: Boolean, videoEnabled: Boolean) -> Unit +typealias CallStateHandler = (pk: PublicKey, callState: EnumSet) -> Unit +typealias VideoBitRateHandler = (pk: PublicKey, bitRate: Int) -> Unit typealias VideoReceiveFrameHandler = ( - pk: String, + pk: PublicKey, width: Int, height: Int, y: ByteArray, @@ -27,8 +27,8 @@ typealias VideoReceiveFrameHandler = ( vStride: Int, ) -> Unit -typealias AudioReceiveFrameHandler = (pk: String, pcm: ShortArray, channels: Int, samplingRate: Int) -> Unit -typealias AudioBitRateHandler = (pk: String, bitRate: Int) -> Unit +typealias AudioReceiveFrameHandler = (pk: PublicKey, pcm: ShortArray, channels: Int, samplingRate: Int) -> Unit +typealias AudioBitRateHandler = (pk: PublicKey, bitRate: Int) -> Unit class ToxAvEventListener @Inject constructor() : ToxAvEventListener { var contactMapping: List> = listOf() @@ -40,7 +40,7 @@ class ToxAvEventListener @Inject constructor() : ToxAvEventListener { var audioReceiveFrameHandler: AudioReceiveFrameHandler = { _, _, _, _ -> } var audioBitRateHandler: AudioBitRateHandler = { _, _ -> } - private fun keyFor(friendNo: Int) = contactMapping.find { it.second == friendNo }!!.first.string() + private fun keyFor(friendNo: Int) = contactMapping.find { it.second == friendNo }!!.first override fun call(friendNo: Int, audioEnabled: Boolean, videoEnabled: Boolean, s: Unit?) = callHandler(keyFor(friendNo), audioEnabled, videoEnabled) diff --git a/domain/src/main/kotlin/tox/ToxEventListener.kt b/domain/src/main/kotlin/tox/ToxEventListener.kt index 8fd8bb7fb..136f30d33 100644 --- a/domain/src/main/kotlin/tox/ToxEventListener.kt +++ b/domain/src/main/kotlin/tox/ToxEventListener.kt @@ -15,26 +15,27 @@ import ltd.evilcorp.core.vo.ConnectionStatus import ltd.evilcorp.core.vo.PublicKey import ltd.evilcorp.core.vo.UserStatus -typealias FriendLosslessPacketHandler = (publicKey: String, data: ByteArray) -> Unit -typealias FileRecvControlHandler = (publicKey: String, fileNo: Int, control: ToxFileControl) -> Unit -typealias FriendStatusMessageHandler = (publicKey: String, message: String) -> Unit -typealias FriendReadReceiptHandler = (publicKey: String, messageId: Int) -> Unit -typealias FriendStatusHandler = (publicKey: String, status: UserStatus) -> Unit -typealias FriendConnectionStatusHandler = (publicKey: String, status: ConnectionStatus) -> Unit -typealias FriendRequestHandler = (publicKey: String, timeDelta: Int, message: String) -> Unit +typealias FriendLosslessPacketHandler = (pk: PublicKey, data: ByteArray) -> Unit +typealias FileRecvControlHandler = (pk: PublicKey, fileNo: Int, control: ToxFileControl) -> Unit +typealias FriendStatusMessageHandler = (pk: PublicKey, message: String) -> Unit +typealias FriendReadReceiptHandler = (pk: PublicKey, messageId: Int) -> Unit +typealias FriendStatusHandler = (pk: PublicKey, status: UserStatus) -> Unit +typealias FriendConnectionStatusHandler = (pk: PublicKey, status: ConnectionStatus) -> Unit +typealias FriendRequestHandler = (pk: PublicKey, timeDelta: Int, message: String) -> Unit typealias FriendMessageHandler = ( - publicKey: String, + pk: PublicKey, messageType: ToxMessageType, timeDelta: Int, message: String, ) -> Unit -typealias FriendNameHandler = (publicKey: String, newName: String) -> Unit -typealias FileRecvChunkHandler = (publicKey: String, fileNo: Int, position: Long, data: ByteArray) -> Unit -typealias FileRecvHandler = (publicKey: String, fileNo: Int, kind: Int, size: Long, name: String) -> Unit -typealias FriendLossyPacketHandler = (publicKey: String, data: ByteArray) -> Unit + +typealias FriendNameHandler = (pk: PublicKey, newName: String) -> Unit +typealias FileRecvChunkHandler = (pk: PublicKey, fileNo: Int, position: Long, data: ByteArray) -> Unit +typealias FileRecvHandler = (pk: PublicKey, fileNo: Int, kind: Int, size: Long, name: String) -> Unit +typealias FriendLossyPacketHandler = (pk: PublicKey, data: ByteArray) -> Unit typealias SelfConnectionStatusHandler = (status: ConnectionStatus) -> Unit -typealias FriendTypingHandler = (publicKey: String, isTyping: Boolean) -> Unit -typealias FileChunkRequestHandler = (publicKey: String, fileNo: Int, position: Long, length: Int) -> Unit +typealias FriendTypingHandler = (pk: PublicKey, isTyping: Boolean) -> Unit +typealias FileChunkRequestHandler = (pk: PublicKey, fileNo: Int, position: Long, length: Int) -> Unit class ToxEventListener @Inject constructor() : ToxCoreEventListener { var contactMapping: List> = listOf() @@ -55,7 +56,7 @@ class ToxEventListener @Inject constructor() : ToxCoreEventListener { var friendTypingHandler: FriendTypingHandler = { _, _ -> } var fileChunkRequestHandler: FileChunkRequestHandler = { _, _, _, _ -> } - private fun keyFor(friendNo: Int) = contactMapping.find { it.second == friendNo }!!.first.string() + private fun keyFor(friendNo: Int) = contactMapping.find { it.second == friendNo }!!.first override fun friendLosslessPacket(friendNo: Int, data: ByteArray, s: Unit?) = friendLosslessPacketHandler(keyFor(friendNo), data) @@ -76,7 +77,7 @@ class ToxEventListener @Inject constructor() : ToxCoreEventListener { friendConnectionStatusHandler(keyFor(friendNo), status.toConnectionStatus()) override fun friendRequest(publicKey: ByteArray, timeDelta: Int, message: ByteArray, s: Unit?) = - friendRequestHandler(publicKey.bytesToHex(), timeDelta, String(message)) + friendRequestHandler(PublicKey.fromBytes(publicKey), timeDelta, String(message)) override fun friendMessage(friendNo: Int, type: ToxMessageType, timeDelta: Int, message: ByteArray, s: Unit?) = friendMessageHandler(keyFor(friendNo), type, timeDelta, String(message)) diff --git a/domain/src/test/kotlin/tox/ToxTypesTest.kt b/domain/src/test/kotlin/tox/ToxTypesTest.kt index f8fceb2e1..d2433088d 100644 --- a/domain/src/test/kotlin/tox/ToxTypesTest.kt +++ b/domain/src/test/kotlin/tox/ToxTypesTest.kt @@ -4,9 +4,9 @@ package ltd.evilcorp.domain.tox -import ltd.evilcorp.core.vo.PublicKey import kotlin.test.Test import kotlin.test.assertEquals +import ltd.evilcorp.core.vo.PublicKey class ToxTypesTest { @Test