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(dashpay): add topup UI and fix UserAlert issues when upgrading #1296

Merged
merged 8 commits into from
Aug 12, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -294,4 +294,8 @@ class EnterAmountFragment : Fragment(R.layout.fragment_enter_amount) {
binding.networkStatusStub.isVisible = !hasInternet
}
}

fun setMessage(message: String) {
binding.messageText.text = message
}
}
12 changes: 12 additions & 0 deletions common/src/main/res/drawable/ic_warning_yellow_circle.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="47dp"
android:height="46dp"
android:viewportWidth="47"
android:viewportHeight="46">
<path
android:pathData="M0.5,23C0.5,10.297 10.797,0 23.5,0C36.202,0 46.5,10.297 46.5,23C46.5,35.702 36.202,46 23.5,46C10.797,46 0.5,35.702 0.5,23Z"
android:fillColor="#FFC043"/>
<path
android:pathData="M15.995,32.536C15.396,32.536 14.875,32.399 14.433,32.126C13.99,31.859 13.645,31.501 13.398,31.052C13.15,30.603 13.026,30.111 13.026,29.577C13.026,29.056 13.16,28.562 13.427,28.093L20.956,14.948C21.229,14.46 21.597,14.092 22.06,13.845C22.522,13.591 23.004,13.464 23.505,13.464C24.006,13.464 24.485,13.588 24.94,13.835C25.396,14.082 25.767,14.453 26.054,14.948L33.573,28.083C33.71,28.324 33.811,28.571 33.876,28.825C33.941,29.073 33.974,29.323 33.974,29.577C33.974,30.111 33.85,30.603 33.603,31.052C33.362,31.501 33.02,31.859 32.577,32.126C32.134,32.399 31.614,32.536 31.015,32.536H15.995ZM23.515,25.544C24.211,25.544 24.579,25.186 24.618,24.47L24.784,20.261C24.797,19.896 24.683,19.6 24.442,19.372C24.201,19.138 23.889,19.021 23.505,19.021C23.114,19.021 22.799,19.134 22.558,19.362C22.323,19.59 22.216,19.886 22.235,20.251L22.392,24.479C22.431,25.189 22.805,25.544 23.515,25.544ZM23.515,29.079C23.905,29.079 24.231,28.965 24.491,28.737C24.758,28.509 24.892,28.21 24.892,27.839C24.892,27.468 24.758,27.168 24.491,26.94C24.231,26.713 23.905,26.599 23.515,26.599C23.124,26.599 22.795,26.713 22.528,26.94C22.261,27.168 22.128,27.468 22.128,27.839C22.128,28.21 22.261,28.509 22.528,28.737C22.802,28.965 23.131,29.079 23.515,29.079Z"
android:fillColor="#ffffff"/>
</vector>
17 changes: 17 additions & 0 deletions common/src/main/res/layout/fragment_enter_amount.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,23 @@
tools:visibility="visible"
tools:text="Insufficient funds" />

<TextView
android:id="@+id/message_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:layout_marginHorizontal="20dp"
android:paddingHorizontal="8dp"
android:gravity="center_horizontal"
android:background="@drawable/gray_button_background"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/bottom_card"
android:maxLines="2"
android:textAlignment="center"
tools:text="0.001 DASH - 10 contacts / profile updates" />


<androidx.cardview.widget.CardView
android:id="@+id/bottom_card"
style="@style/CardViewRaised.Radius15"
Expand Down
11 changes: 11 additions & 0 deletions wallet/res/navigation/nav_send.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@
app:argType="de.schildbach.wallet.data.PaymentIntent" />
</fragment>

<fragment
android:id="@+id/buyCreditsFragment"
android:name="de.schildbach.wallet.ui.send.BuyCreditsFragment"
android:label="BuyCreditsFragment"
tools:layout="@layout/send_coins_fragment">

<argument
android:name="paymentIntent"
app:argType="de.schildbach.wallet.data.PaymentIntent" />
</fragment>

<fragment
android:id="@+id/paymentProtocolFragment"
android:name="de.schildbach.wallet.ui.send.PaymentProtocolFragment"
Expand Down
10 changes: 10 additions & 0 deletions wallet/res/values/strings-extra.xml
Original file line number Diff line number Diff line change
Expand Up @@ -704,4 +704,14 @@
<string name="stale_exchange_rates_error">Prices weren\'t retrieved. Fiat values may be incorrect.</string>
<string name="stale_exchange_rates_stale">Prices are at least 30 minutes old. Fiat values may be incorrect.</string>
<string name="stale_exchange_rates_volatile">Prices have fluctuated more than 50% since the last update.</string>

<!-- credit balance messages / buttons -->
<string name="credit_balance_empty_warning_title">Your credit balance is low</string>
<string name="credit_balance_low_warning_title">Your credit balance is fully depleted</string>
<string name="credit_balance_empty_warning_message">"You can continue to use DashPay for payments but you cannot update your profile or add more contacts until you top up your credit balance</string>
<string name="credit_balance_low_warning_message">Top-up your credits to continue making changes to your profile and adding contacts</string>
<string name="credit_balance_button_maybe_later">Maybe later</string>
<string name="credit_balance_button_buy">Buy Credits</string>
<string name="credit_balance_insufficient_error_message">You don’t have enough funds to buy credits</string>
<string name="buy_credits_estimated_items">%s - %d contacts / %d profile updates</string>
</resources>
2 changes: 1 addition & 1 deletion wallet/src/de/schildbach/wallet/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ public final static class Files {
public static final boolean SUPPORT_BOTH_BACKUP_WARNINGS = false;

// 1,500,000,000 credits
public static final Coin DASH_PAY_FEE = Coin.parseCoin("0.15");
public static final Coin DASH_PAY_FEE = Coin.parseCoin("0.45");
// 150,000,000
public static final Coin DASH_PAY_INVITE_MIN = DASH_PAY_FEE.div(10);

Expand Down
25 changes: 25 additions & 0 deletions wallet/src/de/schildbach/wallet/data/CreditBalanceInfo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package de.schildbach.wallet.data

import org.bitcoinj.core.Coin

/*
* Tests in July 2024: Contact Requests cost about 80,000,000 - 90,000,000
* Profile Updates cost 10,000,000 - 100,000,000
*/
data class CreditBalanceInfo(
val balance: Long
) {
companion object {
const val CREDITS_PER_DUFF = 1_000
const val MAX_OPERATION_COST = 100_000_000.toLong()
val MAX_OPERATION_COST_COIN = MAX_OPERATION_COST / CREDITS_PER_DUFF
const val LOW_BALANCE = MAX_OPERATION_COST * 10
}
fun isBalanceEnough(): Boolean {
return balance >= MAX_OPERATION_COST
}

fun isBalanceWarning(): Boolean {
return balance <= LOW_BALANCE
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ data class BlockchainIdentityData(var creationState: CreationState = CreationSta
DONE,
DONE_AND_DISMISS // this should always be the last value
}

fun finishRestoration() {
this.restoring = false
this.creationStateErrorMessage = null
}
}

@Singleton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ class SendCoinsTaskRunner @Inject constructor(
return sendRequest
}


@VisibleForTesting
fun createSendRequest(
address: Address,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package de.schildbach.wallet.service.platform

import de.schildbach.wallet.Constants
import de.schildbach.wallet.livedata.Resource
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
Expand Down Expand Up @@ -62,7 +61,7 @@ interface PlatformService {
}

class PlatformServiceImplementation @Inject constructor(
walletDataProvider: WalletDataProvider
val walletDataProvider: WalletDataProvider
) : PlatformService {
override val platform = Platform(Constants.NETWORK_PARAMETERS)
override val profiles = Profiles(platform)
Expand All @@ -73,7 +72,7 @@ class PlatformServiceImplementation @Inject constructor(
override val names: Names = platform.names
override val client: DapiClient = platform.client
override val params: NetworkParameters = platform.params

private lateinit var masternodeListManager: SimplifiedMasternodeListManager
companion object {
private val log = LoggerFactory.getLogger(PlatformServiceImplementation::class.java)
}
Expand All @@ -88,7 +87,7 @@ class PlatformServiceImplementation @Inject constructor(
var quorumPublicKey: ByteArray? = null
log.info("searching for quorum: $quorumType, $quorumHash, $coreChainLockedHeight")
Context.propagate(walletDataProvider.wallet!!.context)
Context.get().masternodeListManager.getQuorumListAtTip(
masternodeListManager.getQuorumListAtTip(
LLMQParameters.LLMQType.fromValue(
quorumType
)
Expand Down Expand Up @@ -136,11 +135,11 @@ class PlatformServiceImplementation @Inject constructor(
}

return@withContext success >= 2
//return@withContext Resource.success(true)
}
}

override fun setMasternodeListManager(masternodeListManager: SimplifiedMasternodeListManager) {
this.masternodeListManager = masternodeListManager
platform.setMasternodeListManager(masternodeListManager)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ class PlatformSynchronizationService @Inject constructor(
val contact = EvolutionContact(userId, contactRequest.toUserId.toString())
try {
if (!platformRepo.walletApplication.wallet!!.hasReceivingKeyChain(contact)) {
Context.propagate(walletApplication.wallet!!.context)
log.info("adding accepted/send request to wallet: ${contactRequest.toUserId}")
val contactIdentity = platform.identities.get(contactRequest.toUserId)
var myEncryptionKey = encryptionKey
Expand Down Expand Up @@ -1074,7 +1075,9 @@ class PlatformSynchronizationService @Inject constructor(
}

// first check to see if there is a blockchain identity
if (blockchainIdentityDataDao.load() == null) {
// or if the previous restore is incomplete
val identityData = blockchainIdentityDataDao.load()
if (identityData == null || identityData.restoring) {
log.info("PreDownloadBlocks: checking for existing associated identity")

val identity = platformRepo.getIdentityFromPublicKeyId()
Expand Down
37 changes: 35 additions & 2 deletions wallet/src/de/schildbach/wallet/ui/DashPayUserActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.activity.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import de.schildbach.wallet.data.*
Expand All @@ -37,9 +38,12 @@ import de.schildbach.wallet.ui.transactions.TransactionDetailsDialogFragment
import de.schildbach.wallet.ui.util.InputParser
import de.schildbach.wallet_test.R
import de.schildbach.wallet_test.databinding.ActivityDashpayUserBinding
import kotlinx.coroutines.launch
import org.bitcoinj.core.PrefixedChecksummedBytes
import org.bitcoinj.core.Transaction
import org.bitcoinj.core.VerificationException
import org.bitcoinj.wallet.AuthenticationKeyChain
import org.bitcoinj.wallet.authentication.AuthenticationGroupExtension
import org.dash.wallet.common.WalletDataProvider
import org.dash.wallet.common.data.entity.BlockchainState
import org.dash.wallet.common.services.analytics.AnalyticsConstants
Expand Down Expand Up @@ -156,12 +160,14 @@ class DashPayUserActivity : LockScreenActivity() {

override fun onSendContactRequestClick() {
dashPayViewModel.logEvent(AnalyticsConstants.UsersContacts.SEND_REQUEST)
viewModel.sendContactRequest()
// check credit balance
sendContactRequest()
setResult(RESULT_CODE_CHANGED)
}

override fun onAcceptClick() {
viewModel.sendContactRequest()
// check credit balance
sendContactRequest()
setResult(RESULT_CODE_CHANGED)
}

Expand All @@ -185,6 +191,33 @@ class DashPayUserActivity : LockScreenActivity() {
}
}

fun sendContactRequest() {
lifecycleScope.launch {
val enough = viewModel.hasEnoughCredits()
val shouldWarn = enough.isBalanceWarning()
val isEmpty = enough.isBalanceWarning()

if (shouldWarn || isEmpty) {
val answer = AdaptiveDialog.create(
R.drawable.ic_warning_yellow_circle,
if (isEmpty) getString(R.string.credit_balance_empty_warning_title) else getString(R.string.credit_balance_low_warning_title),
if (isEmpty) getString(R.string.credit_balance_empty_warning_message) else getString(R.string.credit_balance_low_warning_message),
getString(R.string.credit_balance_button_maybe_later),
getString(R.string.credit_balance_button_buy)
).showAsync(this@DashPayUserActivity)

if (answer == true) {
SendCoinsActivity.startBuyCredits(this@DashPayUserActivity)
} else {
if (shouldWarn)
viewModel.sendContactRequest()
}
} else {
viewModel.sendContactRequest()
}
}
}

private fun updateContactRelationUi() {
val userData = viewModel.userData
val state = viewModel.sendContactRequestState.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import de.schildbach.wallet.WalletApplication
import de.schildbach.wallet.data.CreditBalanceInfo
import de.schildbach.wallet.data.UsernameSearchResult
import de.schildbach.wallet.database.entity.DashPayProfile
import de.schildbach.wallet.livedata.Resource
Expand Down Expand Up @@ -100,4 +101,8 @@ class DashPayUserActivityViewModel @Inject constructor(
platformRepo.addOrUpdateDashPayProfile(dashPayProfile)
}
}

suspend fun hasEnoughCredits(): CreditBalanceInfo {
return platformRepo.getIdentityBalance()
}
}
42 changes: 39 additions & 3 deletions wallet/src/de/schildbach/wallet/ui/EditProfileActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.core.net.toUri
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.amulyakhare.textdrawable.TextDrawable
import com.bumptech.glide.Glide
import com.bumptech.glide.signature.ObjectKey
Expand All @@ -54,15 +55,23 @@ import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAuthIO
import com.google.api.services.drive.Drive
import dagger.hilt.android.AndroidEntryPoint
import de.schildbach.wallet.Constants
import de.schildbach.wallet.data.PaymentIntent
import de.schildbach.wallet.database.entity.DashPayProfile
import de.schildbach.wallet.livedata.Status
import de.schildbach.wallet.ui.dashpay.*
import de.schildbach.wallet.ui.dashpay.utils.GoogleDriveService
import de.schildbach.wallet.ui.dashpay.utils.display
import de.schildbach.wallet.ui.dashpay.work.UpdateProfileError
import de.schildbach.wallet.ui.send.SendCoinsActivity
import de.schildbach.wallet_test.R
import de.schildbach.wallet_test.databinding.ActivityEditProfileBinding
import kotlinx.coroutines.launch
import org.bitcoinj.core.Coin
import org.bitcoinj.core.Sha256Hash
import org.bitcoinj.script.ScriptBuilder
import org.bitcoinj.wallet.AuthenticationKeyChain
import org.bitcoinj.wallet.AuthenticationKeyChainGroup
import org.bitcoinj.wallet.authentication.AuthenticationGroupExtension
import org.dash.wallet.common.ui.avatar.ProfilePictureDisplay
import org.dash.wallet.common.ui.avatar.ProfilePictureHelper
import org.dash.wallet.common.ui.avatar.ProfilePictureTransformation
Expand Down Expand Up @@ -172,7 +181,7 @@ class EditProfileActivity : LockScreenActivity() {

})
binding.save.setOnClickListener {
save()
saveButton()
}

binding.profileEditIcon.setOnClickListener {
Expand Down Expand Up @@ -413,7 +422,34 @@ class EditProfileActivity : LockScreenActivity() {
binding.save.isEnabled = !(binding.displayName.text.length > Constants.DISPLAY_NAME_MAX_LENGTH || binding.aboutMe.text.length > Constants.ABOUT_ME_MAX_LENGTH)
}

fun save() {
private fun saveButton() {
lifecycleScope.launch {
val enough = editProfileViewModel.hasEnoughCredits()
val shouldWarn = enough.isBalanceWarning()
val isEmpty = enough.isBalanceWarning()

if (shouldWarn || isEmpty) {
val answer = AdaptiveDialog.create(
R.drawable.ic_warning_yellow_circle,
if (isEmpty) getString(R.string.credit_balance_empty_warning_title) else getString(R.string.credit_balance_low_warning_title),
if (isEmpty) getString(R.string.credit_balance_empty_warning_message) else getString(R.string.credit_balance_low_warning_message),
getString(R.string.credit_balance_button_maybe_later),
getString(R.string.credit_balance_button_buy)
).showAsync(this@EditProfileActivity)

if (answer == true) {
SendCoinsActivity.startBuyCredits(this@EditProfileActivity)
} else {
if (shouldWarn)
save()
}
} else {
save()
}
}
}

private fun save() {
showSaveReminderDialog = false
val displayName = binding.displayName.text.toString().trim()
val publicMessage = binding.aboutMe.text.toString().trim()
Expand Down Expand Up @@ -645,7 +681,7 @@ class EditProfileActivity : LockScreenActivity() {
if (it == true) {
save()
} else {
finish()
super.finish()
}
}
return
Expand Down
Loading
Loading