Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fix crash on sending contacts using QR code #1066

Merged
merged 1 commit into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ class DeepLinkTest {

@Test
fun assertBaseNodeName() {
val deeplink = "tari://${currentNetwork.uriComponent}/${DeepLink.AddBaseNode.addNodeCommand}?${DeepLink.AddBaseNode.nameKey}=base_node_test"
val deeplink = "tari://${currentNetwork.uriComponent}/${DeepLink.AddBaseNode.COMMAND_ADD_NODE}?${DeepLink.AddBaseNode.KEY_NAME}=base_node_test"
val result = deeplinkHandler.handle(deeplink) as? DeepLink.AddBaseNode
assertEquals(result!!.name, "base_node_test")
}

@Test
fun assertBaseNodePeer() {
val deeplink = "tari://${currentNetwork.uriComponent}/${DeepLink.AddBaseNode.addNodeCommand}?${DeepLink.AddBaseNode.peerKey}=$PEER"
val deeplink = "tari://${currentNetwork.uriComponent}/${DeepLink.AddBaseNode.COMMAND_ADD_NODE}?${DeepLink.AddBaseNode.KEY_PEER}=$PEER"
val result = deeplinkHandler.handle(deeplink) as? DeepLink.AddBaseNode
assertEquals(result!!.peer, PEER)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,36 +47,37 @@ sealed class DeepLink {
open fun getParams(): Map<String, String> = emptyMap()
open fun getCommand(): String = ""


// tari://esmeralda/contacts?list[0][alias]=Name&list[0][hex]=hex&list[1][alias]=Name&list[1][hex]=hex
class Contacts(val contacts: List<DeeplinkContact>) : DeepLink() {
data class Contacts(val contacts: List<DeeplinkContact>) : DeepLink() {

constructor(params: Map<String, String>) : this(params.filterKeys { it.startsWith("list[") }
constructor(params: Map<String, String>) : this(
contacts = params.filterKeys { it.startsWith("list[") }
.map { FormatExtractor(it.key, it.value) }
.groupBy { it.index }
.map {
val alias = it.value.firstOrNull { it.name == aliasKey }?.value.orEmpty()
val hex = it.value.firstOrNull { it.name == hexKey }?.value.orEmpty()
.map { param ->
val alias = param.value.firstOrNull { it.name == KEY_ALIAS }?.value.orEmpty()
val hex = param.value.firstOrNull { it.name == KEY_HEX }?.value.orEmpty()
DeeplinkContact(alias, hex)
}
.filter { TariWalletAddress.validate(it.hex) })
.filter { TariWalletAddress.validate(it.hex) },
)

override fun getParams(): Map<String, String> = hashMapOf<String, String>().apply {
contacts.forEachIndexed { index, contact ->
put("list[$index][$aliasKey]", contact.alias)
put("list[$index][$hexKey]", contact.hex)
put("list[$index][$KEY_ALIAS]", contact.alias)
put("list[$index][$KEY_HEX]", contact.hex)
}
}

override fun getCommand(): String = contactsCommand
override fun getCommand(): String = COMMAND_CONTACTS

companion object {
const val contactsCommand = "contacts"
const val aliasKey = "alias"
const val hexKey = "hex"
const val COMMAND_CONTACTS = "contacts"
const val KEY_ALIAS = "alias"
const val KEY_HEX = "hex"
}

class DeeplinkContact(val alias: String, val hex: String)
data class DeeplinkContact(val alias: String, val hex: String)

class FormatExtractor(val key: String, val value: String = "") {
val index: Int
Expand All @@ -94,84 +95,82 @@ sealed class DeepLink {
}
}

class Send(val walletAddressHex: String = "", val amount: MicroTari? = null, val note: String = "") : DeepLink() {
data class Send(val walletAddressHex: String = "", val amount: MicroTari? = null, val note: String = "") : DeepLink() {

constructor(params: Map<String, String>) : this(
params[tariAddressKey].orEmpty(),
params[amountKey]?.let { if (it.isEmpty()) null else MicroTari(it.parseToBigInteger()) },
params[noteKey].orEmpty()
params[KEY_TARI_ADDRESS].orEmpty(),
params[KEY_AMOUNT]?.let { if (it.isEmpty()) null else MicroTari(it.parseToBigInteger()) },
params[KEY_NOTE].orEmpty()
)

override fun getParams(): Map<String, String> = hashMapOf<String, String>().apply {
put(tariAddressKey, walletAddressHex)
put(amountKey, amount?.formattedValue.orEmpty())
put(noteKey, note)
put(KEY_TARI_ADDRESS, walletAddressHex)
put(KEY_AMOUNT, amount?.formattedValue.orEmpty())
put(KEY_NOTE, note)
}

override fun getCommand(): String = sendCommand
override fun getCommand(): String = COMMAND_SEND

companion object {
const val sendCommand = "transactions/send"
const val tariAddressKey = "tariAddress"
const val amountKey = "amount"
const val noteKey = "note"
const val COMMAND_SEND = "transactions/send"
const val KEY_TARI_ADDRESS = "tariAddress"
const val KEY_AMOUNT = "amount"
const val KEY_NOTE = "note"
}
}


class UserProfile(val tariAddressHex: String = "", val alias: String = "") : DeepLink() {
data class UserProfile(val tariAddressHex: String = "", val alias: String = "") : DeepLink() {

constructor(params: Map<String, String>) : this(
params[walletAddressKey].orEmpty(),
params[aliasKey].orEmpty()
params[KEY_WALLET_ADDRESS].orEmpty(),
params[KEY_ALIAS].orEmpty(),
)

override fun getParams(): Map<String, String> = hashMapOf<String, String>().apply {
put(walletAddressKey, tariAddressHex)
put(aliasKey, alias)
put(KEY_WALLET_ADDRESS, tariAddressHex)
put(KEY_ALIAS, alias)
}

override fun getCommand(): String = profileCommand
override fun getCommand(): String = COMMAND_PROFILE

companion object {
const val profileCommand = "profile"
const val walletAddressKey = "tariAddress"
const val aliasKey = "alias"
const val COMMAND_PROFILE = "profile"
const val KEY_WALLET_ADDRESS = "tariAddress"
const val KEY_ALIAS = "alias"
}
}

class AddBaseNode(val name: String = "", val peer: String = "") : DeepLink() {
data class AddBaseNode(val name: String = "", val peer: String = "") : DeepLink() {

constructor(params: Map<String, String>) : this(
params[nameKey].orEmpty(),
params[peerKey].orEmpty(),
params[KEY_NAME].orEmpty(),
params[KEY_PEER].orEmpty(),
)

override fun getParams(): Map<String, String> = hashMapOf<String, String>().apply {
put(nameKey, name)
put(peerKey, peer)
put(KEY_NAME, name)
put(KEY_PEER, peer)
}

override fun getCommand(): String = addNodeCommand
override fun getCommand(): String = COMMAND_ADD_NODE

companion object {
const val addNodeCommand = "base_nodes/add"
const val nameKey = "name"
const val peerKey = "peer"
const val COMMAND_ADD_NODE = "base_nodes/add"
const val KEY_NAME = "name"
const val KEY_PEER = "peer"
}
}

class TorBridges(val torConfigurations: List<TorBridgeConfiguration>): DeepLink() {

}
data class TorBridges(val torConfigurations: List<TorBridgeConfiguration>) : DeepLink()

companion object {

fun getByCommand(command: String, params: Map<String, String>): DeepLink? = when (command) {
Contacts.contactsCommand -> Contacts(params)
Send.sendCommand -> Send(params)
AddBaseNode.addNodeCommand -> AddBaseNode(params)
UserProfile.profileCommand -> UserProfile(params)
Contacts.COMMAND_CONTACTS -> Contacts(params)
Send.COMMAND_SEND -> Send(params)
AddBaseNode.COMMAND_ADD_NODE -> AddBaseNode(params)
UserProfile.COMMAND_PROFILE -> UserProfile(params)
else -> null
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class DeeplinkFormatter @Inject constructor(private val networkRepository: Netwo

var paramentrs = uri.queryParameterNames.associateWith { uri.getQueryParameter(it).orEmpty() }.toMutableMap()
val command = uri.path.orEmpty().trimStart('/')
if (command == DeepLink.Contacts.contactsCommand) {
if (command == DeepLink.Contacts.COMMAND_CONTACTS) {
val values = uri.query.orEmpty().split("&").map {
val (key, value) = it.split("=")
key to value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,6 @@ class DeeplinkViewModel : CommonViewModel() {
}.getOrNull()

private fun addContactsAction(contacts: List<ContactDto>, isQrData: Boolean) {
if (isQrData) {
backPressed.postValue(Unit)
}
contacts.forEach { contactRepository.addContact(it) }
}

Expand Down
18 changes: 8 additions & 10 deletions app/src/main/java/com/tari/android/wallet/di/ApplicationModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,24 @@
package com.tari.android.wallet.di

import android.app.KeyguardManager
import android.content.*
import android.content.ClipboardManager
import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.content.SharedPreferences
import androidx.biometric.BiometricManager
import androidx.core.content.ContextCompat
import com.tari.android.wallet.BuildConfig
import com.tari.android.wallet.application.TariWalletApplication
import com.tari.android.wallet.application.deeplinks.DeeplinkHandler
import com.tari.android.wallet.data.WalletConfig
import com.tari.android.wallet.data.sharedPrefs.SharedPrefsRepository
import com.tari.android.wallet.data.sharedPrefs.baseNode.BaseNodeSharedRepository
import com.tari.android.wallet.data.sharedPrefs.network.NetworkRepository
import com.tari.android.wallet.data.sharedPrefs.network.NetworkRepositoryImpl
import com.tari.android.wallet.data.sharedPrefs.securityStages.SecurityStagesRepository
import com.tari.android.wallet.data.sharedPrefs.tariSettings.TariSettingsSharedRepository
import com.tari.android.wallet.data.sharedPrefs.tor.TorSharedRepository
import com.tari.android.wallet.infrastructure.logging.LoggerAdapter
import com.tari.android.wallet.infrastructure.security.biometric.BiometricAuthenticationService
import com.tari.android.wallet.notification.NotificationHelper
import com.tari.android.wallet.service.service.WalletServiceLauncher
import com.tari.android.wallet.ui.common.domain.PaletteManager
import com.tari.android.wallet.ui.common.domain.ResourceManager
import com.tari.android.wallet.ui.common.gyphy.GiphyAdapter
import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupSettingsRepository
import com.tari.android.wallet.yat.YatSharedRepository
import com.tari.android.wallet.util.ContactUtil
import dagger.Module
import dagger.Provides
import java.io.File
Expand Down Expand Up @@ -123,6 +117,10 @@ class ApplicationModule(private val app: TariWalletApplication) {
@Singleton
fun provideGiphyAdapter(context: Context): GiphyAdapter = GiphyAdapter(context, BuildConfig.GIPHY_KEY)

@Provides
@Singleton
fun provideContactUtil(resourceManager: ResourceManager): ContactUtil = ContactUtil(resourceManager)

companion object {
const val sharedPrefsFileName = "tari_wallet_shared_prefs"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,9 @@
*/
package com.tari.android.wallet.di

import android.content.Context
import android.content.SharedPreferences
import com.tari.android.wallet.BuildConfig
import com.tari.android.wallet.data.sharedPrefs.network.NetworkRepository
import com.tari.android.wallet.ui.common.gyphy.GiphyKeywordsRepository
import com.tari.android.wallet.ui.common.gyphy.repository.GIFRepository
import com.tari.android.wallet.ui.common.gyphy.repository.GiphyRESTRetrofitRepository
import com.tari.android.wallet.ui.fragment.settings.backup.data.BackupSettingsRepository
import dagger.Module
import dagger.Provides
import okhttp3.Interceptor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import com.tari.android.wallet.data.sharedPrefs.bluetooth.BluetoothServerState
import com.tari.android.wallet.data.sharedPrefs.bluetooth.ShareSettingsRepository
import com.tari.android.wallet.extension.addTo
import com.tari.android.wallet.model.TariWalletAddress
import com.tari.android.wallet.ui.fragment.contact_book.data.contacts.ContactDto
import com.tari.android.wallet.util.ContactUtil
import com.welie.blessed.BluetoothCentral
import com.welie.blessed.BluetoothPeripheralManager
import com.welie.blessed.BluetoothPeripheralManagerCallback
Expand All @@ -30,7 +30,8 @@ import javax.inject.Singleton
@Singleton
class TariBluetoothServer @Inject constructor(
private val shareSettingsRepository: ShareSettingsRepository,
val deeplinkHandler: DeeplinkHandler,
private val deeplinkHandler: DeeplinkHandler,
private val contactUtil: ContactUtil,
) : TariBluetoothAdapter() {

private var bluetoothGattServer: BluetoothGattServer? = null
Expand Down Expand Up @@ -147,8 +148,11 @@ class TariBluetoothServer @Inject constructor(
}
val data = deeplinkHandler.getDeeplink(
DeepLink.UserProfile(
sharedPrefsRepository.publicKeyHexString.orEmpty(),
ContactDto.normalizeAlias((sharedPrefsRepository.name.orEmpty() + " " + sharedPrefsRepository.surname).trim(), myWalletAddress),
tariAddressHex = sharedPrefsRepository.publicKeyHexString.orEmpty(),
alias = contactUtil.normalizeAlias(
alias = (sharedPrefsRepository.name.orEmpty() + " " + sharedPrefsRepository.surname).trim(),
walletAddress = myWalletAddress,
),
)
)
logger.i("contactlessPayment: read: whole data: $data")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ open class CommonViewModel : ViewModel() {
private var authorizedAction: (() -> Unit)? = null

val logger: Printer
get() = Logger.t(this::class.simpleName).t(LoggerTags.UI.name)
get() = Logger.t(this::class.simpleName)

val currentTheme = SingleLiveEvent<TariTheme>()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.tari.android.wallet.ui.fragment.contact_book.data.contacts.MergedCont
import com.tari.android.wallet.ui.fragment.contact_book.data.contacts.PhoneContactDto
import com.tari.android.wallet.ui.fragment.contact_book.data.contacts.YatDto
import com.tari.android.wallet.ui.fragment.contact_book.data.localStorage.ContactSharedPrefRepository
import com.tari.android.wallet.util.ContactUtil
import contacts.core.Contacts
import contacts.core.Fields
import contacts.core.entities.NewName
Expand All @@ -43,7 +44,8 @@ import kotlin.system.measureNanoTime
@Singleton
class ContactsRepository @Inject constructor(
val context: Context,
private val contactSharedPrefRepository: ContactSharedPrefRepository
private val contactSharedPrefRepository: ContactSharedPrefRepository,
private val contactUtil: ContactUtil,
) : CommonViewModel() {

val publishSubject = BehaviorSubject.create<MutableList<ContactDto>>()
Expand Down Expand Up @@ -223,9 +225,14 @@ class ContactsRepository @Inject constructor(
viewModelScope.launch(Dispatchers.IO) {
for (item in list.mapNotNull { it.getFFIDto() }) {
val error = WalletError()
service.updateContact(item.walletAddress, item.getAlias(), item.isFavorite, error)
service.updateContact(
item.walletAddress,
contactUtil.normalizeAlias(item.getAlias(), item.walletAddress),
item.isFavorite,
error,
)
if (error.code != WalletError.NoError.code) {
logger.i("Error updating contact: ${error.code}, ${error.code}")
logger.i("Error updating contact: ${error.signature}")
}
}
}
Expand Down Expand Up @@ -454,14 +461,14 @@ class ContactsRepository @Inject constructor(

for (item in contacts) {
val contact = PhoneContact(
item.id,
item.firstName,
item.surname,
item.displayName,
item.avatar,
item.yat,
item.phoneEmojiId,
item.isFavorite
id = item.id,
firstName = item.firstName,
surname = item.surname,
displayName = item.displayName,
yat = item.yat,
emojiId = item.phoneEmojiId,
avatar = item.avatar,
isFavorite = item.isFavorite,
)
saveNamesToPhoneBook(contact)
saveStarredToPhoneBook(contact)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package com.tari.android.wallet.ui.fragment.contact_book.data.contacts

import com.tari.android.wallet.R
import com.tari.android.wallet.data.sharedPrefs.delegates.SerializableTime
import com.tari.android.wallet.model.TariWalletAddress
import com.tari.android.wallet.ui.fragment.contact_book.data.ContactAction
import com.tari.android.wallet.util.extractEmojis
import java.io.Serializable
import java.util.UUID

Expand Down Expand Up @@ -64,14 +62,5 @@ data class ContactDto(

fun getMergedDto(): MergedContactDto? = (contact as? MergedContactDto)

companion object {
fun getDefaultAlias(walletAddress: TariWalletAddress): String =
"Aurora User " + walletAddress.emojiId.extractEmojis().take(3).joinToString("")

fun normalizeAlias(alias: String?, walletAddress: TariWalletAddress): String {
return alias.orEmpty().ifBlank { getDefaultAlias(walletAddress) }
}
}

override fun hashCode(): Int = HashcodeUtils.generate(contact, uuid, lastUsedDate)
}
Loading