aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/AuthenticationFragment.kt44
-rw-r--r--anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/SecurityQuestionFragment.kt27
-rw-r--r--anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/SmsFragment.kt23
-rw-r--r--anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/VideoFragment.kt34
-rw-r--r--anastasis-ui/src/main/java/org/gnu/anastasis/ui/identity/ChangeLocationFragment.kt16
-rw-r--r--build.gradle1
-rw-r--r--cashier/build.gradle3
-rw-r--r--cashier/src/main/java/net/taler/cashier/AboutDialogFragment.kt2
-rw-r--r--cashier/src/main/java/net/taler/cashier/BalanceFragment.kt2
-rw-r--r--cashier/src/main/java/net/taler/cashier/MainViewModel.kt17
-rw-r--r--cashier/src/main/java/net/taler/cashier/Response.kt5
-rw-r--r--cashier/src/main/java/net/taler/cashier/SignedAmount.kt2
-rw-r--r--cashier/src/main/java/net/taler/cashier/config/ConfigManager.kt4
-rw-r--r--cashier/src/main/java/net/taler/cashier/withdraw/WithdrawManager.kt2
-rw-r--r--merchant-lib/build.gradle4
-rw-r--r--merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt27
-rw-r--r--merchant-lib/src/main/java/net/taler/merchantlib/OrderHistory.kt4
-rw-r--r--merchant-lib/src/main/java/net/taler/merchantlib/Orders.kt2
-rw-r--r--merchant-lib/src/main/java/net/taler/merchantlib/Refunds.kt3
-rw-r--r--merchant-lib/src/main/java/net/taler/merchantlib/Response.kt10
-rw-r--r--merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt4
-rw-r--r--merchant-lib/src/test/java/net/taler/merchantlib/MockHttpClient.kt12
-rw-r--r--merchant-terminal/src/main/AndroidManifest.xml1
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt7
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/config/PosConfig.kt2
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/order/LiveOrder.kt2
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/order/Order.kt4
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderStateFragment.kt9
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt2
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundFragment.kt4
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundManager.kt2
-rw-r--r--merchant-terminal/src/test/java/net/taler/merchantpos/order/OrderManagerTest.kt2
m---------multiplatform0
-rw-r--r--taler-kotlin-android/build.gradle2
-rw-r--r--taler-kotlin-android/src/main/AndroidManifest.xml3
-rw-r--r--taler-kotlin-android/src/main/java/net/taler/common/Amount.kt199
-rw-r--r--taler-kotlin-android/src/main/java/net/taler/common/AndroidUtils.kt1
-rw-r--r--taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt2
-rw-r--r--taler-kotlin-android/src/main/java/net/taler/common/TalerUri.kt62
-rw-r--r--taler-kotlin-android/src/main/java/net/taler/common/Time.kt129
-rw-r--r--taler-kotlin-android/src/main/java/net/taler/common/Version.kt70
-rw-r--r--taler-kotlin-android/src/test/java/net/taler/common/AmountTest.kt221
-rw-r--r--taler-kotlin-android/src/test/java/net/taler/common/ContractTermsTest.kt1
-rw-r--r--taler-kotlin-android/src/test/java/net/taler/common/TalerUriTest.kt65
-rw-r--r--taler-kotlin-android/src/test/java/net/taler/common/TestUtils.kt40
-rw-r--r--taler-kotlin-android/src/test/java/net/taler/common/TimeTest.kt46
-rw-r--r--taler-kotlin-android/src/test/java/net/taler/common/VersionTest.kt65
-rw-r--r--wallet/build.gradle4
-rw-r--r--wallet/src/main/AndroidManifest.xml1
-rw-r--r--wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt2
-rw-r--r--wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFees.kt5
-rw-r--r--wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFeesFragment.kt2
-rw-r--r--wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt2
-rw-r--r--wallet/src/main/java/net/taler/wallet/payment/PaymentResponses.kt2
-rw-r--r--wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt6
-rw-r--r--wallet/src/main/java/net/taler/wallet/refund/RefundManager.kt2
-rw-r--r--wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt2
-rw-r--r--wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt4
-rw-r--r--wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt7
-rw-r--r--wallet/src/main/java/net/taler/wallet/withdraw/ManualWithdrawFragment.kt2
-rw-r--r--wallet/src/main/java/net/taler/wallet/withdraw/ManualWithdrawSuccessFragment.kt2
-rw-r--r--wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt2
-rw-r--r--wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt2
64 files changed, 1078 insertions, 161 deletions
diff --git a/.gitmodules b/.gitmodules
index 29c8ad2..e69de29 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +0,0 @@
-[submodule "wallet-kotlin"]
- path = multiplatform
- url = git://git.taler.net/wallet-kotlin.git
diff --git a/anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/AuthenticationFragment.kt b/anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/AuthenticationFragment.kt
index 59d0410..da947b0 100644
--- a/anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/AuthenticationFragment.kt
+++ b/anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/AuthenticationFragment.kt
@@ -29,10 +29,10 @@ import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController
import com.google.android.material.card.MaterialCardView
-import kotlinx.android.synthetic.main.fragment_authentication.*
-import net.taler.lib.common.Amount
+import net.taler.common.Amount
import org.gnu.anastasis.ui.MainViewModel
import org.gnu.anastasis.ui.R
+import org.gnu.anastasis.ui.databinding.FragmentAuthenticationBinding
class AuthenticationFragment : Fragment() {
@@ -40,6 +40,12 @@ class AuthenticationFragment : Fragment() {
private var price: Amount = Amount.zero("KUDOS")
+ private var _binding: FragmentAuthenticationBinding? = null
+
+ // This property is only valid between onCreateView and
+ // onDestroyView.
+ private val binding get() = _binding!!
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -50,46 +56,46 @@ class AuthenticationFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- passwordCard.setOnClickListener {
+ binding.passwordCard.setOnClickListener {
showDialog(
R.id.action_nav_anastasis_authentication_to_securityQuestionFragment,
- passwordCard,
+ binding.passwordCard,
"question_card"
)
}
- postidentCard.setOnClickListener {
+ binding.postidentCard.setOnClickListener {
toggleCard(
- postidentCard,
+ binding.postidentCard,
Amount.fromJSONString("KUDOS:3.5")
)
}
- smsCard.setOnClickListener {
+ binding.smsCard.setOnClickListener {
showDialog(
R.id.action_nav_anastasis_authentication_to_smsFragment,
- smsCard,
+ binding.smsCard,
"sms_card"
)
}
- videoCard.setOnClickListener {
+ binding.videoCard.setOnClickListener {
showDialog(
R.id.action_nav_anastasis_authentication_to_videoFragment,
- videoCard,
+ binding.videoCard,
"video_card"
)
}
viewModel.securityQuestionChecked.observe(viewLifecycleOwner, { checked ->
- passwordCard.isChecked = checked
+ binding.passwordCard.isChecked = checked
updatePrice(checked, Amount.fromJSONString("KUDOS:0.5"))
updateNextButtonState()
})
viewModel.smsChecked.observe(viewLifecycleOwner, { checked ->
- smsCard.isChecked = checked
+ binding.smsCard.isChecked = checked
updatePrice(checked, Amount.fromJSONString("KUDOS:1.0"))
updateNextButtonState()
})
viewModel.videoChecked.observe(viewLifecycleOwner, { checked ->
- videoCard.isChecked = checked
+ binding.videoCard.isChecked = checked
updatePrice(checked, Amount.fromJSONString("KUDOS:2.25"))
updateNextButtonState()
})
@@ -113,16 +119,16 @@ class AuthenticationFragment : Fragment() {
private fun updatePrice(add: Boolean, amount: Amount) {
if (add) price += amount
else price -= amount
- recoveryCostView.text = "Recovery cost: $price"
+ binding.recoveryCostView.text = "Recovery cost: $price"
}
private fun updateNextButtonState() {
var numChecked = 0
- numChecked += if (passwordCard.isChecked) 1 else 0
- numChecked += if (postidentCard.isChecked) 1 else 0
- numChecked += if (smsCard.isChecked) 1 else 0
- numChecked += if (videoCard.isChecked) 1 else 0
- nextAuthButton.isEnabled = numChecked >= 2
+ numChecked += if (binding.passwordCard.isChecked) 1 else 0
+ numChecked += if (binding.postidentCard.isChecked) 1 else 0
+ numChecked += if (binding.smsCard.isChecked) 1 else 0
+ numChecked += if (binding.videoCard.isChecked) 1 else 0
+ binding.nextAuthButton.isEnabled = numChecked >= 2
}
}
diff --git a/anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/SecurityQuestionFragment.kt b/anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/SecurityQuestionFragment.kt
index 7353174..0796610 100644
--- a/anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/SecurityQuestionFragment.kt
+++ b/anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/SecurityQuestionFragment.kt
@@ -23,30 +23,35 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
-import com.google.android.material.transition.MaterialContainerTransform
-import com.google.android.material.transition.MaterialContainerTransform.FADE_MODE_CROSS
-import kotlinx.android.synthetic.main.fragment_security_question.*
import org.gnu.anastasis.ui.MainViewModel
-import org.gnu.anastasis.ui.R
+import org.gnu.anastasis.ui.databinding.FragmentSecurityQuestionBinding
class SecurityQuestionFragment : Fragment() {
private val viewModel: MainViewModel by activityViewModels()
+ private var _binding: FragmentSecurityQuestionBinding? = null
+
+ // This property is only valid between onCreateView and
+ // onDestroyView.
+ private val binding get() = _binding!!
+
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
- sharedElementEnterTransition = MaterialContainerTransform().apply {
- fadeMode = FADE_MODE_CROSS
- }
- return inflater.inflate(R.layout.fragment_security_question, container, false).apply {
- transitionName = "question_card"
- }
+ _binding = FragmentSecurityQuestionBinding.inflate(inflater, container, false)
+ val view = binding.root
+ return view
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- saveQuestionButton.setOnClickListener {
+ binding.saveQuestionButton.setOnClickListener {
viewModel.securityQuestionChecked.value = true
findNavController().popBackStack()
}
diff --git a/anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/SmsFragment.kt b/anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/SmsFragment.kt
index a5d872d..413f472 100644
--- a/anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/SmsFragment.kt
+++ b/anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/SmsFragment.kt
@@ -32,10 +32,11 @@ import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import com.google.android.material.transition.MaterialContainerTransform
import com.google.android.material.transition.MaterialContainerTransform.FADE_MODE_CROSS
-import kotlinx.android.synthetic.main.fragment_sms.*
import org.gnu.anastasis.ui.MainViewModel
import org.gnu.anastasis.ui.PERMISSION_REQUEST_CODE
import org.gnu.anastasis.ui.R
+import org.gnu.anastasis.ui.databinding.FragmentSecurityQuestionBinding
+import org.gnu.anastasis.ui.databinding.FragmentSmsBinding
private const val PERMISSION = Manifest.permission.READ_PHONE_STATE
@@ -43,10 +44,17 @@ class SmsFragment : Fragment() {
private val viewModel: MainViewModel by activityViewModels()
+ private var _binding: FragmentSmsBinding? = null
+
+ // This property is only valid between onCreateView and
+ // onDestroyView.
+ private val binding get() = _binding!!
+
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
+ _binding = FragmentSmsBinding.inflate(inflater, container, false)
sharedElementEnterTransition = MaterialContainerTransform().apply {
fadeMode = FADE_MODE_CROSS
}
@@ -55,11 +63,16 @@ class SmsFragment : Fragment() {
}
}
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- smsView.editText?.setOnFocusChangeListener { _, hasFocus ->
+ binding.smsView.editText?.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) checkPerm()
}
- saveSmsButton.setOnClickListener {
+ binding.saveSmsButton.setOnClickListener {
viewModel.smsChecked.value = true
findNavController().popBackStack()
}
@@ -98,8 +111,8 @@ class SmsFragment : Fragment() {
private fun fillPhoneNumber() {
val telephonyService = requireContext().getSystemService<TelephonyManager>()
telephonyService?.line1Number?.let { phoneNumber ->
- smsView.editText?.setText(phoneNumber)
- smsView.editText?.setSelection(phoneNumber.length)
+ binding.smsView.editText?.setText(phoneNumber)
+ binding.smsView.editText?.setSelection(phoneNumber.length)
}
}
diff --git a/anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/VideoFragment.kt b/anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/VideoFragment.kt
index 6cd80ce..6716f8a 100644
--- a/anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/VideoFragment.kt
+++ b/anastasis-ui/src/main/java/org/gnu/anastasis/ui/authentication/VideoFragment.kt
@@ -35,9 +35,9 @@ import androidx.navigation.fragment.findNavController
import androidx.transition.TransitionManager.beginDelayedTransition
import com.google.android.material.transition.MaterialContainerTransform
import com.google.android.material.transition.MaterialContainerTransform.FADE_MODE_CROSS
-import kotlinx.android.synthetic.main.fragment_video.*
import org.gnu.anastasis.ui.MainViewModel
import org.gnu.anastasis.ui.R
+import org.gnu.anastasis.ui.databinding.FragmentVideoBinding
import java.io.FileDescriptor
private const val REQUEST_IMAGE_CAPTURE = 1
@@ -47,10 +47,17 @@ class VideoFragment : Fragment() {
private val viewModel: MainViewModel by activityViewModels()
+ private var _binding: FragmentVideoBinding? = null
+
+ // This property is only valid between onCreateView and
+ // onDestroyView.
+ private val binding get() = _binding!!
+
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
+ _binding = FragmentVideoBinding.inflate(inflater, container, false)
sharedElementEnterTransition = MaterialContainerTransform().apply {
fadeMode = FADE_MODE_CROSS
}
@@ -59,8 +66,13 @@ class VideoFragment : Fragment() {
}
}
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- takePhotoButton.setOnClickListener {
+ binding.takePhotoButton.setOnClickListener {
val pm = requireContext().packageManager
Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
takePictureIntent.resolveActivity(pm)?.also {
@@ -70,7 +82,7 @@ class VideoFragment : Fragment() {
}
}
}
- choosePhotoButton.setOnClickListener {
+ binding.choosePhotoButton.setOnClickListener {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "image/*"
@@ -80,7 +92,7 @@ class VideoFragment : Fragment() {
)
}
- saveVideoButton.setOnClickListener {
+ binding.saveVideoButton.setOnClickListener {
viewModel.videoChecked.value = true
findNavController().popBackStack()
}
@@ -99,12 +111,14 @@ class VideoFragment : Fragment() {
}
private fun showImage(bitmap: Bitmap) {
- photoView.setImageBitmap(bitmap)
- beginDelayedTransition(view as ViewGroup)
- photoView.visibility = VISIBLE
- takePhotoButton.visibility = GONE
- choosePhotoButton.visibility = GONE
- saveVideoButton.isEnabled = true
+ with (binding) {
+ photoView.setImageBitmap(bitmap)
+ beginDelayedTransition(view as ViewGroup)
+ photoView.visibility = VISIBLE
+ takePhotoButton.visibility = GONE
+ choosePhotoButton.visibility = GONE
+ saveVideoButton.isEnabled = true
+ }
}
private fun getBitmapFromUri(uri: Uri): Bitmap {
diff --git a/anastasis-ui/src/main/java/org/gnu/anastasis/ui/identity/ChangeLocationFragment.kt b/anastasis-ui/src/main/java/org/gnu/anastasis/ui/identity/ChangeLocationFragment.kt
index 5b68d36..00eec11 100644
--- a/anastasis-ui/src/main/java/org/gnu/anastasis/ui/identity/ChangeLocationFragment.kt
+++ b/anastasis-ui/src/main/java/org/gnu/anastasis/ui/identity/ChangeLocationFragment.kt
@@ -23,14 +23,20 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
-import kotlinx.android.synthetic.main.fragment_change_location.*
import org.gnu.anastasis.ui.MainViewModel
import org.gnu.anastasis.ui.R
+import org.gnu.anastasis.ui.databinding.FragmentChangeLocationBinding
class ChangeLocationFragment : Fragment() {
private val viewModel: MainViewModel by activityViewModels()
+ private var _binding: FragmentChangeLocationBinding? = null
+
+ // This property is only valid between onCreateView and
+ // onDestroyView.
+ private val binding get() = _binding!!
+
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
@@ -40,16 +46,16 @@ class ChangeLocationFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- switzerlandView.setOnClickListener {
+ binding.switzerlandView.setOnClickListener {
changeCountry(LOCATIONS[0])
}
- germanyView.setOnClickListener {
+ binding.germanyView.setOnClickListener {
changeCountry(LOCATIONS[1])
}
- usaView.setOnClickListener {
+ binding.usaView.setOnClickListener {
changeCountry(LOCATIONS[2])
}
- indiaView.setOnClickListener {
+ binding.indiaView.setOnClickListener {
changeCountry(LOCATIONS[3])
}
}
diff --git a/build.gradle b/build.gradle
index b274284..20b5c78 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,4 +1,5 @@
buildscript {
+ ext.kotlin_version = '1.6.10'
ext.ktor_version = '2.0.1'
ext.nav_version = '2.4.2'
ext.material_version = '1.6.0'
diff --git a/cashier/build.gradle b/cashier/build.gradle
index 1c9c2b6..ea59f23 100644
--- a/cashier/build.gradle
+++ b/cashier/build.gradle
@@ -85,6 +85,9 @@ dependencies {
implementation "io.ktor:ktor-client:$ktor_version"
implementation "io.ktor:ktor-client-okhttp:$ktor_version"
implementation "io.ktor:ktor-client-serialization-jvm:$ktor_version"
+ implementation "io.ktor:ktor-serialization-kotlinx-json:$ktor_version"
+ implementation "io.ktor:ktor-client-content-negotiation:$ktor_version"
+ implementation "io.ktor:ktor-server-call-logging:$ktor_version"
testImplementation "junit:junit:$junit_version"
diff --git a/cashier/src/main/java/net/taler/cashier/AboutDialogFragment.kt b/cashier/src/main/java/net/taler/cashier/AboutDialogFragment.kt
index cdea792..3da49d2 100644
--- a/cashier/src/main/java/net/taler/cashier/AboutDialogFragment.kt
+++ b/cashier/src/main/java/net/taler/cashier/AboutDialogFragment.kt
@@ -30,7 +30,7 @@ import net.taler.cashier.BuildConfig.VERSION_NAME
import net.taler.cashier.config.VERSION_BANK
import net.taler.cashier.databinding.FragmentAboutDialogBinding
import net.taler.cashier.databinding.FragmentBalanceBinding
-import net.taler.lib.common.Version
+import net.taler.common.Version
class AboutDialogFragment : DialogFragment() {
diff --git a/cashier/src/main/java/net/taler/cashier/BalanceFragment.kt b/cashier/src/main/java/net/taler/cashier/BalanceFragment.kt
index 002301c..fa9600b 100644
--- a/cashier/src/main/java/net/taler/cashier/BalanceFragment.kt
+++ b/cashier/src/main/java/net/taler/cashier/BalanceFragment.kt
@@ -32,10 +32,10 @@ import net.taler.cashier.BalanceFragmentDirections.Companion.actionBalanceFragme
import net.taler.cashier.databinding.FragmentBalanceBinding
import net.taler.cashier.withdraw.LastTransaction
import net.taler.cashier.withdraw.WithdrawStatus
+import net.taler.common.Amount
import net.taler.common.exhaustive
import net.taler.common.fadeIn
import net.taler.common.fadeOut
-import net.taler.lib.common.Amount
sealed class BalanceResult {
class Error(val msg: String) : BalanceResult()
diff --git a/cashier/src/main/java/net/taler/cashier/MainViewModel.kt b/cashier/src/main/java/net/taler/cashier/MainViewModel.kt
index 253c7d5..1c819b9 100644
--- a/cashier/src/main/java/net/taler/cashier/MainViewModel.kt
+++ b/cashier/src/main/java/net/taler/cashier/MainViewModel.kt
@@ -24,17 +24,16 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import io.ktor.client.HttpClient
import io.ktor.client.engine.okhttp.OkHttp
-import io.ktor.client.features.json.JsonFeature
-import io.ktor.client.features.json.serializer.KotlinxSerializer
+import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
+import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
-import kotlinx.serialization.json.Json
import net.taler.cashier.HttpHelper.makeJsonGetRequest
import net.taler.cashier.config.ConfigManager
import net.taler.cashier.withdraw.WithdrawManager
+import net.taler.common.Amount
+import net.taler.common.AmountParserException
import net.taler.common.isOnline
-import net.taler.lib.common.Amount
-import net.taler.lib.common.AmountParserException
private val TAG = MainViewModel::class.java.simpleName
@@ -46,12 +45,8 @@ class MainViewModel(private val app: Application) : AndroidViewModel(app) {
retryOnConnectionFailure(true)
}
}
- install(JsonFeature) {
- serializer = KotlinxSerializer(
- Json {
- ignoreUnknownKeys = true
- }
- )
+ install(ContentNegotiation) {
+ json()
}
}
val configManager = ConfigManager(app, viewModelScope, httpClient)
diff --git a/cashier/src/main/java/net/taler/cashier/Response.kt b/cashier/src/main/java/net/taler/cashier/Response.kt
index 6a72604..89b7b33 100644
--- a/cashier/src/main/java/net/taler/cashier/Response.kt
+++ b/cashier/src/main/java/net/taler/cashier/Response.kt
@@ -18,8 +18,9 @@ package net.taler.cashier
import android.content.Context
import android.util.Log
+import io.ktor.client.call.body
import io.ktor.client.call.receive
-import io.ktor.client.features.ResponseException
+import io.ktor.client.plugins.ResponseException
import io.ktor.http.HttpStatusCode
import kotlinx.serialization.Serializable
import net.taler.common.isOnline
@@ -47,7 +48,7 @@ class Response<out T> private constructor(
val response = e.response
return try {
Log.e("TEST", "TRY RECEIVE $response")
- val error: Error = response.receive()
+ val error: Error = response.body()
"Error ${error.code}: ${error.hint}"
} catch (ex: Exception) {
"Status code: ${response.status.value}"
diff --git a/cashier/src/main/java/net/taler/cashier/SignedAmount.kt b/cashier/src/main/java/net/taler/cashier/SignedAmount.kt
index e79acfd..4f624ae 100644
--- a/cashier/src/main/java/net/taler/cashier/SignedAmount.kt
+++ b/cashier/src/main/java/net/taler/cashier/SignedAmount.kt
@@ -16,7 +16,7 @@
package net.taler.cashier
-import net.taler.lib.common.Amount
+import net.taler.common.Amount
data class SignedAmount(
val positive: Boolean,
diff --git a/cashier/src/main/java/net/taler/cashier/config/ConfigManager.kt b/cashier/src/main/java/net/taler/cashier/config/ConfigManager.kt
index 0718963..882348f 100644
--- a/cashier/src/main/java/net/taler/cashier/config/ConfigManager.kt
+++ b/cashier/src/main/java/net/taler/cashier/config/ConfigManager.kt
@@ -39,8 +39,8 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.taler.cashier.Response
import net.taler.cashier.Response.Companion.response
+import net.taler.common.Version
import net.taler.common.getIncompatibleStringOrNull
-import net.taler.lib.common.Version
val VERSION_BANK = Version(0, 0, 0)
private const val PREF_NAME = "net.taler.cashier.prefs"
@@ -126,7 +126,7 @@ class ConfigManager(
val balanceResponse = response {
val authUrl = "${config.bankUrl}/accounts/${config.username}"
Log.d(TAG, "Checking auth: $authUrl")
- httpClient.get<Unit>(authUrl) {
+ httpClient.get(authUrl) {
header(Authorization, config.basicAuth)
}
}
diff --git a/cashier/src/main/java/net/taler/cashier/withdraw/WithdrawManager.kt b/cashier/src/main/java/net/taler/cashier/withdraw/WithdrawManager.kt
index 5d34bba..360bded 100644
--- a/cashier/src/main/java/net/taler/cashier/withdraw/WithdrawManager.kt
+++ b/cashier/src/main/java/net/taler/cashier/withdraw/WithdrawManager.kt
@@ -34,9 +34,9 @@ import net.taler.cashier.HttpJsonResult.Error
import net.taler.cashier.HttpJsonResult.Success
import net.taler.cashier.MainViewModel
import net.taler.cashier.R
+import net.taler.common.Amount
import net.taler.common.QrCodeManager.makeQrCode
import net.taler.common.isOnline
-import net.taler.lib.common.Amount
import org.json.JSONObject
import java.util.concurrent.TimeUnit.MINUTES
import java.util.concurrent.TimeUnit.SECONDS
diff --git a/merchant-lib/build.gradle b/merchant-lib/build.gradle
index c554b77..c31009d 100644
--- a/merchant-lib/build.gradle
+++ b/merchant-lib/build.gradle
@@ -57,9 +57,11 @@ dependencies {
api "io.ktor:ktor-client:$ktor_version"
api "io.ktor:ktor-client-okhttp:$ktor_version"
api "io.ktor:ktor-client-serialization-jvm:$ktor_version"
+ api "io.ktor:ktor-client-content-negotiation:$ktor_version"
+ implementation "io.ktor:ktor-serialization-kotlinx-json:$ktor_version"
+ implementation "io.ktor:ktor-server-call-logging:$ktor_version"
testImplementation "junit:junit:$junit_version"
testImplementation "io.ktor:ktor-client-mock-jvm:$ktor_version"
- testImplementation "io.ktor:ktor-client-logging-jvm:$ktor_version"
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.1'
}
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt
index 0d22f91..d973813 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt
@@ -17,21 +17,22 @@
package net.taler.merchantlib
import io.ktor.client.HttpClient
+import io.ktor.client.call.body
import io.ktor.client.engine.okhttp.OkHttp
-import io.ktor.client.features.json.JsonFeature
-import io.ktor.client.features.json.serializer.KotlinxSerializer
+import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.delete
import io.ktor.client.request.get
import io.ktor.client.request.header
import io.ktor.client.request.post
+import io.ktor.client.request.setBody
import io.ktor.http.ContentType.Application.Json
import io.ktor.http.HttpHeaders.Authorization
import io.ktor.http.contentType
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
-import kotlinx.serialization.json.Json
import net.taler.merchantlib.Response.Companion.response
+import io.ktor.serialization.kotlinx.json.*
class MerchantApi(
private val httpClient: HttpClient = getDefaultHttpClient(),
@@ -52,8 +53,8 @@ class MerchantApi(
httpClient.post(merchantConfig.urlFor("private/orders")) {
header(Authorization, "ApiKey ${merchantConfig.apiKey}")
contentType(Json)
- body = orderRequest
- } as PostOrderResponse
+ setBody(orderRequest)
+ }.body()
}
}
@@ -64,7 +65,7 @@ class MerchantApi(
response {
httpClient.get(merchantConfig.urlFor("private/orders/$orderId")) {
header(Authorization, "ApiKey ${merchantConfig.apiKey}")
- } as CheckPaymentResponse
+ }.body()
}
}
@@ -97,7 +98,7 @@ class MerchantApi(
httpClient.post(merchantConfig.urlFor("private/orders/$orderId/refund")) {
header(Authorization, "ApiKey ${merchantConfig.apiKey}")
contentType(Json)
- body = request
+ setBody(request)
} as RefundResponse
}
}
@@ -109,15 +110,7 @@ fun getDefaultHttpClient(): HttpClient = HttpClient(OkHttp) {
retryOnConnectionFailure(true)
}
}
- install(JsonFeature) {
- serializer = getSerializer()
+ install(ContentNegotiation) {
+ json()
}
}
-
-fun getSerializer() = KotlinxSerializer(
- Json {
- encodeDefaults = false
- ignoreUnknownKeys = true
- classDiscriminator = "order_status"
- }
-)
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/OrderHistory.kt b/merchant-lib/src/main/java/net/taler/merchantlib/OrderHistory.kt
index dfd989b..b1ff5b1 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/OrderHistory.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/OrderHistory.kt
@@ -18,8 +18,8 @@ package net.taler.merchantlib
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
-import net.taler.lib.common.Amount
-import net.taler.lib.common.Timestamp
+import net.taler.common.Amount
+import net.taler.common.Timestamp
@Serializable
data class OrderHistory(
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/Orders.kt b/merchant-lib/src/main/java/net/taler/merchantlib/Orders.kt
index 166ca3c..9572e07 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/Orders.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/Orders.kt
@@ -19,8 +19,8 @@ package net.taler.merchantlib
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.taler.common.ContractTerms
+import net.taler.common.Duration
import net.taler.lib.android.CustomClassDiscriminator
-import net.taler.lib.common.Duration
@Serializable
data class PostOrderRequest(
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/Refunds.kt b/merchant-lib/src/main/java/net/taler/merchantlib/Refunds.kt
index b78b571..58073fa 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/Refunds.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/Refunds.kt
@@ -18,8 +18,7 @@ package net.taler.merchantlib
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
-import net.taler.lib.common.Amount
-
+import net.taler.common.Amount
@Serializable
data class RefundRequest(
/**
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/Response.kt b/merchant-lib/src/main/java/net/taler/merchantlib/Response.kt
index 6fd0e37..b7ba1ac 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/Response.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/Response.kt
@@ -16,10 +16,10 @@
package net.taler.merchantlib
-import io.ktor.client.call.receive
-import io.ktor.client.features.ClientRequestException
-import io.ktor.client.features.ResponseException
-import io.ktor.client.features.ServerResponseException
+import io.ktor.client.call.body
+import io.ktor.client.plugins.ClientRequestException
+import io.ktor.client.plugins.ResponseException
+import io.ktor.client.plugins.ServerResponseException
import kotlinx.serialization.Serializable
class Response<out T> private constructor(
@@ -72,7 +72,7 @@ class Response<out T> private constructor(
private suspend fun getExceptionString(e: ResponseException): String {
val response = e.response
return try {
- val error: Error = response.receive()
+ val error: Error = response.body()
"Error ${error.code}: ${error.hint}"
} catch (ex: Exception) {
"Status code: ${response.status.value}"
diff --git a/merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt b/merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt
index 6abacfd..8a45c9f 100644
--- a/merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt
+++ b/merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt
@@ -20,10 +20,10 @@ import io.ktor.http.HttpStatusCode.Companion.NotFound
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestCoroutineDispatcher
+import net.taler.common.Amount
import net.taler.common.ContractProduct
import net.taler.common.ContractTerms
-import net.taler.lib.common.Amount
-import net.taler.lib.common.Timestamp
+import net.taler.common.Timestamp
import net.taler.merchantlib.MockHttpClient.giveJsonResponse
import net.taler.merchantlib.MockHttpClient.httpClient
import org.junit.Assert.assertEquals
diff --git a/merchant-lib/src/test/java/net/taler/merchantlib/MockHttpClient.kt b/merchant-lib/src/test/java/net/taler/merchantlib/MockHttpClient.kt
index c8e6f22..880228c 100644
--- a/merchant-lib/src/test/java/net/taler/merchantlib/MockHttpClient.kt
+++ b/merchant-lib/src/test/java/net/taler/merchantlib/MockHttpClient.kt
@@ -20,11 +20,6 @@ import io.ktor.client.HttpClient
import io.ktor.client.engine.mock.MockEngine
import io.ktor.client.engine.mock.MockEngineConfig
import io.ktor.client.engine.mock.respond
-import io.ktor.client.features.json.JsonFeature
-import io.ktor.client.features.logging.LogLevel
-import io.ktor.client.features.logging.Logger
-import io.ktor.client.features.logging.Logging
-import io.ktor.client.features.logging.SIMPLE
import io.ktor.http.ContentType.Application.Json
import io.ktor.http.HttpStatusCode
import io.ktor.http.Url
@@ -38,13 +33,6 @@ import org.junit.Assert.assertEquals
object MockHttpClient {
val httpClient = HttpClient(MockEngine) {
- install(JsonFeature) {
- serializer = getSerializer()
- }
- install(Logging) {
- logger = Logger.SIMPLE
- level = LogLevel.ALL
- }
engine {
addHandler { error("No response handler set") }
}
diff --git a/merchant-terminal/src/main/AndroidManifest.xml b/merchant-terminal/src/main/AndroidManifest.xml
index eb7940f..f7b4929 100644
--- a/merchant-terminal/src/main/AndroidManifest.xml
+++ b/merchant-terminal/src/main/AndroidManifest.xml
@@ -39,6 +39,7 @@
<activity
android:name=".MainActivity"
android:label="@string/app_name"
+ android:exported="true"
android:screenOrientation="landscape"
android:theme="@style/AppTheme.NoActionBar"
tools:ignore="LockedOrientationActivity">
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt
index 165bb8e..4327f4e 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt
@@ -26,7 +26,8 @@ import androidx.annotation.WorkerThread
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import io.ktor.client.HttpClient
-import io.ktor.client.features.ClientRequestException
+import io.ktor.client.call.body
+import io.ktor.client.plugins.ClientRequestException
import io.ktor.client.request.get
import io.ktor.client.request.header
import io.ktor.http.HttpHeaders.Authorization
@@ -34,8 +35,8 @@ import io.ktor.http.HttpStatusCode.Companion.Unauthorized
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
+import net.taler.common.Version
import net.taler.common.getIncompatibleStringOrNull
-import net.taler.lib.common.Version
import net.taler.merchantlib.ConfigResponse
import net.taler.merchantlib.MerchantApi
import net.taler.merchantlib.MerchantConfig
@@ -105,7 +106,7 @@ class ConfigManager(
val credentials = "${config.username}:${config.password}"
val auth = ("Basic ${encodeToString(credentials.toByteArray(), NO_WRAP)}")
header(Authorization, auth)
- }
+ }.body()
val merchantConfig = posConfig.merchantConfig
// get config from merchant backend API
api.getConfig(merchantConfig.baseUrl).handleSuspend(::onNetworkError) {
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/PosConfig.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/PosConfig.kt
index 971f92c..1f1ab74 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/config/PosConfig.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/config/PosConfig.kt
@@ -19,10 +19,10 @@ package net.taler.merchantpos.config
import android.os.Build.VERSION.SDK_INT
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
+import net.taler.common.Amount
import net.taler.common.ContractProduct
import net.taler.common.Product
import net.taler.common.TalerUtils
-import net.taler.lib.common.Amount
import net.taler.merchantlib.MerchantConfig
import java.util.UUID
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/order/LiveOrder.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/order/LiveOrder.kt
index f48c1db..ad9af74 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/order/LiveOrder.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/order/LiveOrder.kt
@@ -20,8 +20,8 @@ import androidx.annotation.UiThread
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
+import net.taler.common.Amount
import net.taler.common.CombinedLiveData
-import net.taler.lib.common.Amount
import net.taler.merchantpos.config.Category
import net.taler.merchantpos.config.ConfigProduct
import net.taler.merchantpos.order.RestartState.DISABLED
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/order/Order.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/order/Order.kt
index 0bea20c..9860dbd 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/order/Order.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/order/Order.kt
@@ -16,10 +16,10 @@
package net.taler.merchantpos.order
+import net.taler.common.Amount
import net.taler.common.ContractTerms
+import net.taler.common.Timestamp
import net.taler.common.now
-import net.taler.lib.common.Amount
-import net.taler.lib.common.Timestamp
import net.taler.merchantpos.config.Category
import net.taler.merchantpos.config.ConfigProduct
import java.net.URLEncoder
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderStateFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderStateFragment.kt
index d86f504..c4a5228 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderStateFragment.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderStateFragment.kt
@@ -26,6 +26,7 @@ import androidx.recyclerview.selection.SelectionPredicates
import androidx.recyclerview.selection.SelectionTracker
import androidx.recyclerview.selection.StorageStrategy
import androidx.recyclerview.widget.LinearLayoutManager
+import net.taler.common.Amount
import net.taler.common.fadeIn
import net.taler.common.fadeOut
import net.taler.merchantpos.MainViewModel
@@ -82,11 +83,11 @@ class OrderStateFragment : Fragment() {
liveOrder.selectOrderLine(item)
}
})
- liveOrder.order.observe(viewLifecycleOwner, { order ->
+ liveOrder.order.observe(viewLifecycleOwner) { order ->
if (order == null) return@observe
onOrderChanged(order, tracker)
- })
- liveOrder.orderTotal.observe(viewLifecycleOwner, { orderTotal ->
+ }
+ liveOrder.orderTotal.observe(viewLifecycleOwner) { orderTotal: Amount ->
if (orderTotal.isZero()) {
ui.totalView.fadeOut()
ui.totalView.text = null
@@ -94,7 +95,7 @@ class OrderStateFragment : Fragment() {
ui.totalView.text = getString(R.string.order_total, orderTotal)
ui.totalView.fadeIn()
}
- })
+ }
}
override fun onSaveInstanceState(outState: Bundle) {
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt
index 98161db..d703a31 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt
@@ -26,8 +26,8 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
+import net.taler.common.Duration
import net.taler.common.assertUiThread
-import net.taler.lib.common.Duration
import net.taler.merchantlib.CheckPaymentResponse
import net.taler.merchantlib.MerchantApi
import net.taler.merchantlib.PostOrderRequest
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundFragment.kt
index bb98dbd..03c786f 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundFragment.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundFragment.kt
@@ -24,12 +24,12 @@ import androidx.annotation.StringRes
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
+import net.taler.common.Amount
+import net.taler.common.AmountParserException
import net.taler.common.fadeIn
import net.taler.common.fadeOut
import net.taler.common.navigate
import net.taler.common.showError
-import net.taler.lib.common.Amount
-import net.taler.lib.common.AmountParserException
import net.taler.merchantlib.OrderHistoryEntry
import net.taler.merchantpos.MainViewModel
import net.taler.merchantpos.R
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundManager.kt
index 8b3efca..849dbaa 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundManager.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundManager.kt
@@ -21,8 +21,8 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
+import net.taler.common.Amount
import net.taler.common.assertUiThread
-import net.taler.lib.common.Amount
import net.taler.merchantlib.MerchantApi
import net.taler.merchantlib.OrderHistoryEntry
import net.taler.merchantlib.RefundRequest
diff --git a/merchant-terminal/src/test/java/net/taler/merchantpos/order/OrderManagerTest.kt b/merchant-terminal/src/test/java/net/taler/merchantpos/order/OrderManagerTest.kt
index ca48b6e..bb8dcb7 100644
--- a/merchant-terminal/src/test/java/net/taler/merchantpos/order/OrderManagerTest.kt
+++ b/merchant-terminal/src/test/java/net/taler/merchantpos/order/OrderManagerTest.kt
@@ -20,7 +20,7 @@ import android.app.Application
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.runBlocking
-import net.taler.lib.common.Amount
+import net.taler.common.Amount
import net.taler.merchantlib.MerchantConfig
import net.taler.merchantpos.R
import net.taler.merchantpos.config.Category
diff --git a/multiplatform b/multiplatform
deleted file mode 160000
-Subproject cb92566166884efdaad5e93ef25e2de1f303461
diff --git a/taler-kotlin-android/build.gradle b/taler-kotlin-android/build.gradle
index e47efe9..b4590c4 100644
--- a/taler-kotlin-android/build.gradle
+++ b/taler-kotlin-android/build.gradle
@@ -58,8 +58,6 @@ android {
}
dependencies {
- api project(":multiplatform:common")
-
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.core:core-ktx:1.7.0'
implementation "androidx.constraintlayout:constraintlayout:$constraintlayout_version"
diff --git a/taler-kotlin-android/src/main/AndroidManifest.xml b/taler-kotlin-android/src/main/AndroidManifest.xml
index c1f6146..f475748 100644
--- a/taler-kotlin-android/src/main/AndroidManifest.xml
+++ b/taler-kotlin-android/src/main/AndroidManifest.xml
@@ -18,9 +18,6 @@
xmlns:tools="http://schemas.android.com/tools">
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
-
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.NFC" />
</manifest>
diff --git a/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt b/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt
new file mode 100644
index 0000000..a36c0ab
--- /dev/null
+++ b/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt
@@ -0,0 +1,199 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2020 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.common
+
+import kotlinx.serialization.KSerializer
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.Serializer
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+import kotlin.math.floor
+import kotlin.math.pow
+import kotlin.math.roundToInt
+
+public class AmountParserException(msg: String? = null, cause: Throwable? = null) : Exception(msg, cause)
+public class AmountOverflowException(msg: String? = null, cause: Throwable? = null) : Exception(msg, cause)
+
+@Serializable(with = KotlinXAmountSerializer::class)
+public data class Amount(
+ /**
+ * name of the currency using either a three-character ISO 4217 currency code,
+ * or a regional currency identifier starting with a "*" followed by at most 10 characters.
+ * ISO 4217 exponents in the name are not supported,
+ * although the "fraction" is corresponds to an ISO 4217 exponent of 6.
+ */
+ val currency: String,
+
+ /**
+ * The integer part may be at most 2^52.
+ * Note that "1" here would correspond to 1 EUR or 1 USD, depending on currency, not 1 cent.
+ */
+ val value: Long,
+
+ /**
+ * Unsigned 32 bit fractional value to be added to value representing
+ * an additional currency fraction, in units of one hundred millionth (1e-8)
+ * of the base currency value. For example, a fraction
+ * of 50_000_000 would correspond to 50 cents.
+ */
+ val fraction: Int
+) : Comparable<Amount> {
+
+ public companion object {
+
+ private const val FRACTIONAL_BASE: Int = 100000000 // 1e8
+
+ private val REGEX_CURRENCY = Regex("""^[-_*A-Za-z0-9]{1,12}$""")
+ public val MAX_VALUE: Long = 2.0.pow(52).toLong()
+ private const val MAX_FRACTION_LENGTH = 8
+ public const val MAX_FRACTION: Int = 99_999_999
+
+ public fun zero(currency: String): Amount {
+ return Amount(checkCurrency(currency), 0, 0)
+ }
+
+ public fun fromJSONString(str: String): Amount {
+ val split = str.split(":")
+ if (split.size != 2) throw AmountParserException("Invalid Amount Format")
+ return fromString(split[0], split[1])
+ }
+
+ public fun fromString(currency: String, str: String): Amount {
+ // value
+ val valueSplit = str.split(".")
+ val value = checkValue(valueSplit[0].toLongOrNull())
+ // fraction
+ val fraction: Int = if (valueSplit.size > 1) {
+ val fractionStr = valueSplit[1]
+ if (fractionStr.length > MAX_FRACTION_LENGTH)
+ throw AmountParserException("Fraction $fractionStr too long")
+ val fraction = "0.$fractionStr".toDoubleOrNull()
+ ?.times(FRACTIONAL_BASE)
+ ?.roundToInt()
+ checkFraction(fraction)
+ } else 0
+ return Amount(checkCurrency(currency), value, fraction)
+ }
+
+ public fun min(currency: String): Amount = Amount(currency, 0, 1)
+ public fun max(currency: String): Amount = Amount(currency, MAX_VALUE, MAX_FRACTION)
+
+
+ internal fun checkCurrency(currency: String): String {
+ if (!REGEX_CURRENCY.matches(currency))
+ throw AmountParserException("Invalid currency: $currency")
+ return currency
+ }
+
+ internal fun checkValue(value: Long?): Long {
+ if (value == null || value > MAX_VALUE)
+ throw AmountParserException("Value $value greater than $MAX_VALUE")
+ return value
+ }
+
+ internal fun checkFraction(fraction: Int?): Int {
+ if (fraction == null || fraction > MAX_FRACTION)
+ throw AmountParserException("Fraction $fraction greater than $MAX_FRACTION")
+ return fraction
+ }
+
+ }
+
+ public val amountStr: String
+ get() = if (fraction == 0) "$value" else {
+ var f = fraction
+ var fractionStr = ""
+ while (f > 0) {
+ fractionStr += f / (FRACTIONAL_BASE / 10)
+ f = (f * 10) % FRACTIONAL_BASE
+ }
+ "$value.$fractionStr"
+ }
+
+ public operator fun plus(other: Amount): Amount {
+ check(currency == other.currency) { "Can only subtract from same currency" }
+ val resultValue = value + other.value + floor((fraction + other.fraction).toDouble() / FRACTIONAL_BASE).toLong()
+ if (resultValue > MAX_VALUE)
+ throw AmountOverflowException()
+ val resultFraction = (fraction + other.fraction) % FRACTIONAL_BASE
+ return Amount(currency, resultValue, resultFraction)
+ }
+
+ public operator fun times(factor: Int): Amount {
+ // TODO consider replacing with a faster implementation
+ if (factor == 0) return zero(currency)
+ var result = this
+ for (i in 1 until factor) result += this
+ return result
+ }
+
+ public operator fun minus(other: Amount): Amount {
+ check(currency == other.currency) { "Can only subtract from same currency" }
+ var resultValue = value
+ var resultFraction = fraction
+ if (resultFraction < other.fraction) {
+ if (resultValue < 1L)
+ throw AmountOverflowException()
+ resultValue--
+ resultFraction += FRACTIONAL_BASE
+ }
+ check(resultFraction >= other.fraction)
+ resultFraction -= other.fraction
+ if (resultValue < other.value)
+ throw AmountOverflowException()
+ resultValue -= other.value
+ return Amount(currency, resultValue, resultFraction)
+ }
+
+ public fun isZero(): Boolean {
+ return value == 0L && fraction == 0
+ }
+
+ public fun toJSONString(): String {
+ return "$currency:$amountStr"
+ }
+
+ override fun toString(): String {
+ return "$amountStr $currency"
+ }
+
+ override fun compareTo(other: Amount): Int {
+ check(currency == other.currency) { "Can only compare amounts with the same currency" }
+ when {
+ value == other.value -> {
+ if (fraction < other.fraction) return -1
+ if (fraction > other.fraction) return 1
+ return 0
+ }
+ value < other.value -> return -1
+ else -> return 1
+ }
+ }
+
+}
+
+@Suppress("EXPERIMENTAL_API_USAGE")
+@Serializer(forClass = Amount::class)
+internal object KotlinXAmountSerializer : KSerializer<Amount> {
+ override fun serialize(encoder: Encoder, value: Amount) {
+ encoder.encodeString(value.toJSONString())
+ }
+
+ override fun deserialize(decoder: Decoder): Amount {
+ return Amount.fromJSONString(decoder.decodeString())
+ }
+}
diff --git a/taler-kotlin-android/src/main/java/net/taler/common/AndroidUtils.kt b/taler-kotlin-android/src/main/java/net/taler/common/AndroidUtils.kt
index 4ac2e73..d86e744 100644
--- a/taler-kotlin-android/src/main/java/net/taler/common/AndroidUtils.kt
+++ b/taler-kotlin-android/src/main/java/net/taler/common/AndroidUtils.kt
@@ -49,7 +49,6 @@ import androidx.navigation.fragment.findNavController
import com.github.pedrovgs.lynx.LynxActivity
import com.github.pedrovgs.lynx.LynxConfig
import net.taler.lib.android.ErrorBottomSheet
-import net.taler.lib.common.Version
fun View.fadeIn(endAction: () -> Unit = {}) {
if (visibility == VISIBLE && alpha == 1f) return
diff --git a/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt b/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
index fb30692..910cc36 100644
--- a/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
+++ b/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
@@ -20,8 +20,6 @@ import android.os.Build
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.taler.common.TalerUtils.getLocalizedString
-import net.taler.lib.common.Amount
-import net.taler.lib.common.Timestamp
@Serializable
data class ContractTerms(
diff --git a/taler-kotlin-android/src/main/java/net/taler/common/TalerUri.kt b/taler-kotlin-android/src/main/java/net/taler/common/TalerUri.kt
new file mode 100644
index 0000000..a1b7225
--- /dev/null
+++ b/taler-kotlin-android/src/main/java/net/taler/common/TalerUri.kt
@@ -0,0 +1,62 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2020 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.common
+
+import java.util.Locale
+
+public object TalerUri {
+
+ private const val SCHEME = "taler://"
+ private const val SCHEME_INSECURE = "taler+http://"
+ private const val AUTHORITY_PAY = "pay"
+ private const val AUTHORITY_WITHDRAW = "withdraw"
+ private const val AUTHORITY_REFUND = "refund"
+ private const val AUTHORITY_TIP = "tip"
+
+ public data class WithdrawUriResult(
+ val bankIntegrationApiBaseUrl: String,
+ val withdrawalOperationId: String
+ )
+
+ /**
+ * Parses a withdraw URI and returns a bank status URL or null if the URI was invalid.
+ */
+ public fun parseWithdrawUri(uri: String): WithdrawUriResult? {
+ val (resultScheme, prefix) = when {
+ uri.startsWith(SCHEME, ignoreCase = true) -> {
+ Pair("https://", "${SCHEME}${AUTHORITY_WITHDRAW}/")
+ }
+ uri.startsWith(SCHEME_INSECURE, ignoreCase = true) -> {
+ Pair("http://", "${SCHEME_INSECURE}${AUTHORITY_WITHDRAW}/")
+ }
+ else -> return null
+ }
+ if (!uri.startsWith(prefix)) return null
+ val parts = uri.let {
+ (if (it.endsWith("/")) it.dropLast(1) else it).substring(prefix.length).split('/')
+ }
+ if (parts.size < 2) return null
+ val host = parts[0].lowercase(Locale.ROOT)
+ val pathSegments = parts.slice(1 until parts.size - 1).joinToString("/")
+ val withdrawId = parts.last()
+ if (withdrawId.isBlank()) return null
+ val url = "${resultScheme}${host}/${pathSegments}"
+
+ return WithdrawUriResult(url, withdrawId)
+ }
+
+}
diff --git a/taler-kotlin-android/src/main/java/net/taler/common/Time.kt b/taler-kotlin-android/src/main/java/net/taler/common/Time.kt
new file mode 100644
index 0000000..61bbce8
--- /dev/null
+++ b/taler-kotlin-android/src/main/java/net/taler/common/Time.kt
@@ -0,0 +1,129 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2020 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.common
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.builtins.serializer
+import kotlinx.serialization.json.JsonElement
+import kotlinx.serialization.json.JsonPrimitive
+import kotlinx.serialization.json.JsonTransformingSerializer
+import kotlinx.serialization.json.contentOrNull
+import kotlinx.serialization.json.jsonPrimitive
+import kotlinx.serialization.json.longOrNull
+import kotlin.math.max
+
+@Serializable
+data class Timestamp(
+ @SerialName("t_ms")
+ @Serializable(NeverSerializer::class)
+ val old_ms: Long? = null,
+ @SerialName("t_s")
+ @Serializable(NeverSerializer::class)
+ private val s: Long? = null,
+) : Comparable<Timestamp> {
+
+ constructor(ms: Long) : this(ms, null)
+
+ companion object {
+ private const val NEVER: Long = -1
+ fun now(): Timestamp = Timestamp(System.currentTimeMillis())
+ fun never(): Timestamp = Timestamp(NEVER)
+ }
+
+ val ms: Long = if (s != null) {
+ s * 1000L
+ } else if (old_ms !== null) {
+ old_ms
+ } else {
+ throw Exception("timestamp didn't have t_s or t_ms")
+ }
+
+
+ /**
+ * Returns a copy of this [Timestamp] rounded to seconds.
+ */
+ fun truncateSeconds(): Timestamp {
+ if (ms == NEVER) return Timestamp(ms)
+ return Timestamp((ms / 1000L) * 1000L)
+ }
+
+ operator fun minus(other: Timestamp): Duration = when {
+ ms == NEVER -> Duration(Duration.FOREVER)
+ other.ms == NEVER -> throw Error("Invalid argument for timestamp comparision")
+ ms < other.ms -> Duration(0)
+ else -> Duration(ms - other.ms)
+ }
+
+ operator fun minus(other: Duration): Timestamp = when {
+ ms == NEVER -> this
+ other.ms == Duration.FOREVER -> Timestamp(0)
+ else -> Timestamp(max(0, ms - other.ms))
+ }
+
+ override fun compareTo(other: Timestamp): Int {
+ return if (ms == NEVER) {
+ if (other.ms == NEVER) 0
+ else 1
+ } else {
+ if (other.ms == NEVER) -1
+ else ms.compareTo(other.ms)
+ }
+ }
+
+}
+
+@Serializable
+data class Duration(
+ @SerialName("d_ms")
+ @Serializable(ForeverSerializer::class) val old_ms: Long? = null,
+ @SerialName("d_s")
+ @Serializable(ForeverSerializer::class)
+ private val s: Long? = null,
+) {
+ val ms: Long = if (s != null) {
+ s * 1000L
+ } else if (old_ms !== null) {
+ old_ms
+ } else {
+ throw Exception("duration didn't have d_s or d_ms")
+ }
+
+ constructor(ms: Long) : this(ms, null)
+
+ companion object {
+ internal const val FOREVER: Long = -1
+ fun forever(): Duration = Duration(FOREVER)
+ }
+}
+
+internal abstract class MinusOneSerializer(private val keyword: String) :
+ JsonTransformingSerializer<Long>(Long.serializer()) {
+
+ override fun transformDeserialize(element: JsonElement): JsonElement {
+ return if (element.jsonPrimitive.contentOrNull == keyword) return JsonPrimitive(-1)
+ else super.transformDeserialize(element)
+ }
+
+ override fun transformSerialize(element: JsonElement): JsonElement {
+ return if (element.jsonPrimitive.longOrNull == -1L) return JsonPrimitive(keyword)
+ else element
+ }
+}
+
+internal object NeverSerializer : MinusOneSerializer("never")
+internal object ForeverSerializer : MinusOneSerializer("forever")
diff --git a/taler-kotlin-android/src/main/java/net/taler/common/Version.kt b/taler-kotlin-android/src/main/java/net/taler/common/Version.kt
new file mode 100644
index 0000000..f46913e
--- /dev/null
+++ b/taler-kotlin-android/src/main/java/net/taler/common/Version.kt
@@ -0,0 +1,70 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2020 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.common
+
+import kotlin.math.sign
+
+/**
+ * Semantic versioning, but libtool-style.
+ * See https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
+ */
+public data class Version(
+ val current: Int,
+ val revision: Int,
+ val age: Int
+) {
+ public companion object {
+ public fun parse(v: String): Version? {
+ val elements = v.split(":")
+ if (elements.size != 3) return null
+ val (currentStr, revisionStr, ageStr) = elements
+ val current = currentStr.toIntOrNull()
+ val revision = revisionStr.toIntOrNull()
+ val age = ageStr.toIntOrNull()
+ if (current == null || revision == null || age == null) return null
+ return Version(current, revision, age)
+ }
+ }
+
+ /**
+ * Compare two libtool-style versions.
+ *
+ * Returns a [VersionMatchResult] or null if the given version was null.
+ */
+ public fun compare(other: Version?): VersionMatchResult? {
+ if (other == null) return null
+ val compatible = current - age <= other.current &&
+ current >= other.current - other.age
+ val currentCmp = sign((current - other.current).toDouble()).toInt()
+ return VersionMatchResult(compatible, currentCmp)
+ }
+
+ /**
+ * Result of comparing two libtool versions.
+ */
+ public data class VersionMatchResult(
+ /**
+ * Is the first version compatible with the second?
+ */
+ val compatible: Boolean,
+ /**
+ * Is the first version older (-1), newer (+1) or identical (0)?
+ */
+ val currentCmp: Int
+ )
+
+}
diff --git a/taler-kotlin-android/src/test/java/net/taler/common/AmountTest.kt b/taler-kotlin-android/src/test/java/net/taler/common/AmountTest.kt
new file mode 100644
index 0000000..3343b52
--- /dev/null
+++ b/taler-kotlin-android/src/test/java/net/taler/common/AmountTest.kt
@@ -0,0 +1,221 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2020 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.common
+
+import kotlin.random.Random
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Assert.assertFalse
+import org.junit.Test
+
+class AmountTest {
+
+ companion object {
+ fun getRandomAmount() = getRandomAmount(getRandomString(1, Random.nextInt(1, 12)))
+ fun getRandomAmount(currency: String): Amount {
+ val value = Random.nextLong(0, Amount.MAX_VALUE)
+ val fraction = Random.nextInt(0, Amount.MAX_FRACTION)
+ return Amount(currency, value, fraction)
+ }
+ }
+
+ @Test
+ fun testFromJSONString() {
+ var str = "TESTKUDOS:23.42"
+ var amount = Amount.fromJSONString(str)
+ assertEquals(str, amount.toJSONString())
+ assertEquals("TESTKUDOS", amount.currency)
+ assertEquals(23, amount.value)
+ assertEquals((0.42 * 1e8).toInt(), amount.fraction)
+ assertEquals("23.42 TESTKUDOS", amount.toString())
+
+ str = "EUR:500000000.00000001"
+ amount = Amount.fromJSONString(str)
+ assertEquals(str, amount.toJSONString())
+ assertEquals("EUR", amount.currency)
+ assertEquals(500000000, amount.value)
+ assertEquals(1, amount.fraction)
+ assertEquals("500000000.00000001 EUR", amount.toString())
+
+ str = "EUR:1500000000.00000003"
+ amount = Amount.fromJSONString(str)
+ assertEquals(str, amount.toJSONString())
+ assertEquals("EUR", amount.currency)
+ assertEquals(1500000000, amount.value)
+ assertEquals(3, amount.fraction)
+ assertEquals("1500000000.00000003 EUR", amount.toString())
+ }
+
+ @Test
+ fun testFromJSONStringAcceptsMaxValuesRejectsAbove() {
+ val maxValue = 4503599627370496
+ val str = "TESTKUDOS123:$maxValue.99999999"
+ val amount = Amount.fromJSONString(str)
+ assertEquals(str, amount.toJSONString())
+ assertEquals("TESTKUDOS123", amount.currency)
+ assertEquals(maxValue, amount.value)
+ assertEquals("$maxValue.99999999 TESTKUDOS123", amount.toString())
+
+ // longer currency not accepted
+ assertThrows<AmountParserException>("longer currency was accepted") {
+ Amount.fromJSONString("TESTKUDOS1234:$maxValue.99999999")
+ }
+
+ // max value + 1 not accepted
+ assertThrows<AmountParserException>("max value + 1 was accepted") {
+ Amount.fromJSONString("TESTKUDOS123:${maxValue + 1}.99999999")
+ }
+
+ // max fraction + 1 not accepted
+ assertThrows<AmountParserException>("max fraction + 1 was accepted") {
+ Amount.fromJSONString("TESTKUDOS123:$maxValue.999999990")
+ }
+ }
+
+ @Test
+ fun testFromJSONStringRejections() {
+ assertThrows<AmountParserException> {
+ Amount.fromJSONString("TESTKUDOS:0,5")
+ }
+ assertThrows<AmountParserException> {
+ Amount.fromJSONString("+TESTKUDOS:0.5")
+ }
+ assertThrows<AmountParserException> {
+ Amount.fromJSONString("0.5")
+ }
+ assertThrows<AmountParserException> {
+ Amount.fromJSONString(":0.5")
+ }
+ assertThrows<AmountParserException> {
+ Amount.fromJSONString("EUR::0.5")
+ }
+ assertThrows<AmountParserException> {
+ Amount.fromJSONString("EUR:.5")
+ }
+ }
+
+ @Test
+ fun testAddition() {
+ assertEquals(
+ Amount.fromJSONString("EUR:2"),
+ Amount.fromJSONString("EUR:1") + Amount.fromJSONString("EUR:1")
+ )
+ assertEquals(
+ Amount.fromJSONString("EUR:3"),
+ Amount.fromJSONString("EUR:1.5") + Amount.fromJSONString("EUR:1.5")
+ )
+ assertEquals(
+ Amount.fromJSONString("EUR:500000000.00000002"),
+ Amount.fromJSONString("EUR:500000000.00000001") + Amount.fromJSONString("EUR:0.00000001")
+ )
+ assertThrows<AmountOverflowException>("addition didn't overflow") {
+ Amount.fromJSONString("EUR:4503599627370496.99999999") + Amount.fromJSONString("EUR:0.00000001")
+ }
+ assertThrows<AmountOverflowException>("addition didn't overflow") {
+ Amount.fromJSONString("EUR:4000000000000000") + Amount.fromJSONString("EUR:4000000000000000")
+ }
+ }
+
+ @Test
+ fun testTimes() {
+ assertEquals(
+ Amount.fromJSONString("EUR:2"),
+ Amount.fromJSONString("EUR:2") * 1
+ )
+ assertEquals(
+ Amount.fromJSONString("EUR:2"),
+ Amount.fromJSONString("EUR:1") * 2
+ )
+ assertEquals(
+ Amount.fromJSONString("EUR:4.5"),
+ Amount.fromJSONString("EUR:1.5") * 3
+ )
+ assertEquals(Amount.fromJSONString("EUR:0"), Amount.fromJSONString("EUR:1.11") * 0)
+ assertEquals(Amount.fromJSONString("EUR:1.11"), Amount.fromJSONString("EUR:1.11") * 1)
+ assertEquals(Amount.fromJSONString("EUR:2.22"), Amount.fromJSONString("EUR:1.11") * 2)
+ assertEquals(Amount.fromJSONString("EUR:3.33"), Amount.fromJSONString("EUR:1.11") * 3)
+ assertEquals(Amount.fromJSONString("EUR:4.44"), Amount.fromJSONString("EUR:1.11") * 4)
+ assertEquals(Amount.fromJSONString("EUR:5.55"), Amount.fromJSONString("EUR:1.11") * 5)
+ assertEquals(
+ Amount.fromJSONString("EUR:1500000000.00000003"),
+ Amount.fromJSONString("EUR:500000000.00000001") * 3
+ )
+ assertThrows<AmountOverflowException>("times didn't overflow") {
+ Amount.fromJSONString("EUR:4000000000000000") * 2
+ }
+ }
+
+ @Test
+ fun testSubtraction() {
+ assertEquals(
+ Amount.fromJSONString("EUR:0"),
+ Amount.fromJSONString("EUR:1") - Amount.fromJSONString("EUR:1")
+ )
+ assertEquals(
+ Amount.fromJSONString("EUR:1.5"),
+ Amount.fromJSONString("EUR:3") - Amount.fromJSONString("EUR:1.5")
+ )
+ assertEquals(
+ Amount.fromJSONString("EUR:500000000.00000001"),
+ Amount.fromJSONString("EUR:500000000.00000002") - Amount.fromJSONString("EUR:0.00000001")
+ )
+ assertThrows<AmountOverflowException>("subtraction didn't underflow") {
+ Amount.fromJSONString("EUR:23.42") - Amount.fromJSONString("EUR:42.23")
+ }
+ assertThrows<AmountOverflowException>("subtraction didn't underflow") {
+ Amount.fromJSONString("EUR:0.5") - Amount.fromJSONString("EUR:0.50000001")
+ }
+ }
+
+ @Test
+ fun testIsZero() {
+ assertTrue(Amount.zero("EUR").isZero())
+ assertTrue(Amount.fromJSONString("EUR:0").isZero())
+ assertTrue(Amount.fromJSONString("EUR:0.0").isZero())
+ assertTrue(Amount.fromJSONString("EUR:0.00000").isZero())
+ assertTrue((Amount.fromJSONString("EUR:1.001") - Amount.fromJSONString("EUR:1.001")).isZero())
+
+ assertFalse(Amount.fromJSONString("EUR:0.00000001").isZero())
+ assertFalse(Amount.fromJSONString("EUR:1.0").isZero())
+ assertFalse(Amount.fromJSONString("EUR:0001.0").isZero())
+ }
+
+ @Test
+ fun testComparision() {
+ assertTrue(Amount.fromJSONString("EUR:0") <= Amount.fromJSONString("EUR:0"))
+ assertTrue(Amount.fromJSONString("EUR:0") <= Amount.fromJSONString("EUR:0.00000001"))
+ assertTrue(Amount.fromJSONString("EUR:0") < Amount.fromJSONString("EUR:0.00000001"))
+ assertTrue(Amount.fromJSONString("EUR:0") < Amount.fromJSONString("EUR:1"))
+ assertEquals(Amount.fromJSONString("EUR:0"), Amount.fromJSONString("EUR:0"))
+ assertEquals(Amount.fromJSONString("EUR:42"), Amount.fromJSONString("EUR:42"))
+ assertEquals(
+ Amount.fromJSONString("EUR:42.00000001"),
+ Amount.fromJSONString("EUR:42.00000001")
+ )
+ assertTrue(Amount.fromJSONString("EUR:42.00000001") >= Amount.fromJSONString("EUR:42.00000001"))
+ assertTrue(Amount.fromJSONString("EUR:42.00000002") >= Amount.fromJSONString("EUR:42.00000001"))
+ assertTrue(Amount.fromJSONString("EUR:42.00000002") > Amount.fromJSONString("EUR:42.00000001"))
+ assertTrue(Amount.fromJSONString("EUR:0.00000002") > Amount.fromJSONString("EUR:0.00000001"))
+ assertTrue(Amount.fromJSONString("EUR:0.00000001") > Amount.fromJSONString("EUR:0"))
+ assertTrue(Amount.fromJSONString("EUR:2") > Amount.fromJSONString("EUR:1"))
+
+ assertThrows<IllegalStateException>("could compare amounts with different currencies") {
+ Amount.fromJSONString("EUR:0.5") < Amount.fromJSONString("USD:0.50000001")
+ }
+ }
+
+}
diff --git a/taler-kotlin-android/src/test/java/net/taler/common/ContractTermsTest.kt b/taler-kotlin-android/src/test/java/net/taler/common/ContractTermsTest.kt
index 3a2cdb4..f7b83a9 100644
--- a/taler-kotlin-android/src/test/java/net/taler/common/ContractTermsTest.kt
+++ b/taler-kotlin-android/src/test/java/net/taler/common/ContractTermsTest.kt
@@ -18,7 +18,6 @@ package net.taler.common
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
-import net.taler.lib.common.Timestamp
import org.junit.Assert.assertEquals
import org.junit.Test
diff --git a/taler-kotlin-android/src/test/java/net/taler/common/TalerUriTest.kt b/taler-kotlin-android/src/test/java/net/taler/common/TalerUriTest.kt
new file mode 100644
index 0000000..128f707
--- /dev/null
+++ b/taler-kotlin-android/src/test/java/net/taler/common/TalerUriTest.kt
@@ -0,0 +1,65 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2020 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.common
+
+import net.taler.common.TalerUri.parseWithdrawUri
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+
+class TalerUriTest {
+
+ @Test
+ fun testParseWithdrawUri() {
+ // correct parsing
+ var uri = "taler://withdraw/bank.example.com/12345"
+ var expected = TalerUri.WithdrawUriResult("https://bank.example.com/", "12345")
+ assertEquals(expected, parseWithdrawUri(uri))
+
+ // correct parsing with insecure http
+ uri = "taler+http://withdraw/bank.example.org/foo"
+ expected = TalerUri.WithdrawUriResult("http://bank.example.org/", "foo")
+ assertEquals(expected, parseWithdrawUri(uri))
+
+ // correct parsing with long path
+ uri = "taler://withdraw/bank.example.com/foo/bar/23/42/1337/1234567890"
+ expected =
+ TalerUri.WithdrawUriResult("https://bank.example.com/foo/bar/23/42/1337", "1234567890")
+ assertEquals(expected, parseWithdrawUri(uri))
+
+ // rejects incorrect scheme
+ uri = "talerx://withdraw/bank.example.com/12345"
+ assertNull(parseWithdrawUri(uri))
+
+ // rejects incorrect authority
+ uri = "taler://withdrawx/bank.example.com/12345"
+ assertNull(parseWithdrawUri(uri))
+
+ // rejects incorrect authority with insecure http
+ uri = "taler+http://withdrawx/bank.example.com/12345"
+ assertNull(parseWithdrawUri(uri))
+
+ // rejects empty withdrawalId
+ uri = "taler://withdraw/bank.example.com//"
+ assertNull(parseWithdrawUri(uri))
+
+ // rejects empty path and withdrawalId
+ uri = "taler://withdraw/bank.example.com////"
+ assertNull(parseWithdrawUri(uri))
+ }
+
+}
diff --git a/taler-kotlin-android/src/test/java/net/taler/common/TestUtils.kt b/taler-kotlin-android/src/test/java/net/taler/common/TestUtils.kt
new file mode 100644
index 0000000..b0f191d
--- /dev/null
+++ b/taler-kotlin-android/src/test/java/net/taler/common/TestUtils.kt
@@ -0,0 +1,40 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2020 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.common
+
+import kotlin.random.Random
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+
+private val charPool: List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9')
+fun getRandomString(minLength: Int = 1, maxLength: Int = Random.nextInt(0, 1337)) =
+ (minLength..maxLength)
+ .map { Random.nextInt(0, charPool.size) }
+ .map(charPool::get)
+ .joinToString("")
+
+inline fun <reified T : Throwable> assertThrows(
+ msg: String? = null,
+ function: () -> Any
+) {
+ try {
+ function.invoke()
+ fail(msg)
+ } catch (e: Exception) {
+ assertTrue(e is T)
+ }
+}
diff --git a/taler-kotlin-android/src/test/java/net/taler/common/TimeTest.kt b/taler-kotlin-android/src/test/java/net/taler/common/TimeTest.kt
new file mode 100644
index 0000000..beda621
--- /dev/null
+++ b/taler-kotlin-android/src/test/java/net/taler/common/TimeTest.kt
@@ -0,0 +1,46 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2020 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.common
+
+import kotlinx.serialization.json.Json.Default.decodeFromString
+import kotlinx.serialization.json.Json.Default.encodeToString
+import kotlin.random.Random
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+// TODO test other functionality of Timestamp and Duration
+class TimeTest {
+
+ @Test
+ fun testSerialize() {
+ for (i in 0 until 42) {
+ val t = Random.nextLong()
+ assertEquals("""{"t_ms":$t}""", encodeToString(Timestamp.serializer(), Timestamp(t)))
+ }
+ assertEquals("""{"t_ms":"never"}""", encodeToString(Timestamp.serializer(), Timestamp.never()))
+ }
+
+ @Test
+ fun testDeserialize() {
+ for (i in 0 until 42) {
+ val t = Random.nextLong()
+ assertEquals(Timestamp(t), decodeFromString(Timestamp.serializer(), """{ "t_ms": $t }"""))
+ }
+ assertEquals(Timestamp.never(), decodeFromString(Timestamp.serializer(), """{ "t_ms": "never" }"""))
+ }
+
+}
diff --git a/taler-kotlin-android/src/test/java/net/taler/common/VersionTest.kt b/taler-kotlin-android/src/test/java/net/taler/common/VersionTest.kt
new file mode 100644
index 0000000..d8d7149
--- /dev/null
+++ b/taler-kotlin-android/src/test/java/net/taler/common/VersionTest.kt
@@ -0,0 +1,65 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2020 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.common
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+
+
+class VersionTest {
+
+ @Test
+ fun testParse() {
+ assertNull(Version.parse(""))
+ assertNull(Version.parse("foo"))
+ assertNull(Version.parse("foo:bar:foo"))
+ assertNull(Version.parse("0:0:0:"))
+ assertNull(Version.parse("0:0:"))
+ assertEquals(Version(0, 0, 0), Version.parse("0:0:0"))
+ assertEquals(Version(1, 2, 3), Version.parse("1:2:3"))
+ assertEquals(Version(1337, 42, 23), Version.parse("1337:42:23"))
+ }
+
+ @Test
+ fun testComparision() {
+ assertEquals(
+ Version.VersionMatchResult(true, 0),
+ Version.parse("0:0:0")!!.compare(Version.parse("0:0:0"))
+ )
+ assertEquals(
+ Version.VersionMatchResult(true, -1),
+ Version.parse("0:0:0")!!.compare(Version.parse("1:0:1"))
+ )
+ assertEquals(
+ Version.VersionMatchResult(true, -1),
+ Version.parse("0:0:0")!!.compare(Version.parse("1:5:1"))
+ )
+ assertEquals(
+ Version.VersionMatchResult(false, -1),
+ Version.parse("0:0:0")!!.compare(Version.parse("1:5:0"))
+ )
+ assertEquals(
+ Version.VersionMatchResult(false, 1),
+ Version.parse("1:0:0")!!.compare(Version.parse("0:5:0"))
+ )
+ assertEquals(
+ Version.VersionMatchResult(true, 0),
+ Version.parse("1:0:1")!!.compare(Version.parse("1:5:1"))
+ )
+ }
+
+}
diff --git a/wallet/build.gradle b/wallet/build.gradle
index 20bdd49..b0a8b97 100644
--- a/wallet/build.gradle
+++ b/wallet/build.gradle
@@ -91,7 +91,7 @@ android {
}
composeOptions {
- kotlinCompilerExtensionVersion '1.0.5'
+ kotlinCompilerExtensionVersion '1.1.1'
}
buildFeatures {
@@ -121,7 +121,7 @@ dependencies {
implementation project(":anastasis-ui")
implementation 'net.taler:akono:0.2'
- implementation "org.jetbrains.kotlin:kotlin-reflect:1.6.0"
+ implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation 'androidx.preference:preference-ktx:1.2.0'
implementation "com.google.android.material:material:$material_version"
diff --git a/wallet/src/main/AndroidManifest.xml b/wallet/src/main/AndroidManifest.xml
index 285908a..b0e4ffe 100644
--- a/wallet/src/main/AndroidManifest.xml
+++ b/wallet/src/main/AndroidManifest.xml
@@ -43,6 +43,7 @@
<activity
android:name=".MainActivity"
+ android:exported="true"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
diff --git a/wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt b/wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt
index 09ae353..24ee1a1 100644
--- a/wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt
+++ b/wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt
@@ -25,7 +25,7 @@ import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.Adapter
import kotlinx.serialization.Serializable
-import net.taler.lib.common.Amount
+import net.taler.common.Amount
import net.taler.wallet.R
import net.taler.wallet.balances.BalanceAdapter.BalanceViewHolder
diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFees.kt b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFees.kt
index 1da9b49..7824d1e 100644
--- a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFees.kt
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFees.kt
@@ -16,8 +16,9 @@
package net.taler.wallet.exchanges
-import net.taler.lib.common.Amount
-import net.taler.lib.common.Timestamp
+import net.taler.common.Amount
+import net.taler.common.Timestamp
+
data class CoinFee(
val coin: Amount,
diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFeesFragment.kt b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFeesFragment.kt
index 1ea32dd..d8242f3 100644
--- a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFeesFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFeesFragment.kt
@@ -27,9 +27,9 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.RecyclerView.Adapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import net.taler.common.Amount
import net.taler.common.toRelativeTime
import net.taler.common.toShortDate
-import net.taler.lib.common.Amount
import net.taler.wallet.MainViewModel
import net.taler.wallet.R
import net.taler.wallet.databinding.FragmentExchangeFeesBinding
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt b/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
index 34a8023..ae091df 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
@@ -22,8 +22,8 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
+import net.taler.common.Amount
import net.taler.common.ContractTerms
-import net.taler.lib.common.Amount
import net.taler.wallet.TAG
import net.taler.wallet.backend.WalletBackendApi
import net.taler.wallet.backend.TalerErrorInfo
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PaymentResponses.kt b/wallet/src/main/java/net/taler/wallet/payment/PaymentResponses.kt
index 4b908b5..fb206be 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PaymentResponses.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PaymentResponses.kt
@@ -18,9 +18,9 @@ package net.taler.wallet.payment
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
+import net.taler.common.Amount
import net.taler.common.ContractTerms
import net.taler.lib.android.CustomClassDiscriminator
-import net.taler.lib.common.Amount
import net.taler.wallet.backend.TalerErrorInfo
@Serializable
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
index 700e158..d5c3eaf 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
@@ -28,10 +28,10 @@ import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
+import net.taler.common.Amount
import net.taler.common.ContractTerms
import net.taler.common.fadeIn
import net.taler.common.fadeOut
-import net.taler.lib.common.Amount
import net.taler.wallet.MainViewModel
import net.taler.wallet.R
import net.taler.wallet.databinding.FragmentPromptPaymentBinding
@@ -49,7 +49,7 @@ class PromptPaymentFragment : Fragment(), ProductImageClickListener {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
+ savedInstanceState: Bundle?,
): View {
ui = FragmentPromptPaymentBinding.inflate(inflater, container, false)
return ui.root
@@ -131,7 +131,7 @@ class PromptPaymentFragment : Fragment(), ProductImageClickListener {
}
}
- private fun showOrder(contractTerms: ContractTerms, amount:Amount, totalFees: Amount? = null) {
+ private fun showOrder(contractTerms: ContractTerms, amount: Amount, totalFees: Amount? = null) {
ui.details.orderView.text = contractTerms.summary
adapter.setItems(contractTerms.products)
ui.details.productsList.fadeIn()
diff --git a/wallet/src/main/java/net/taler/wallet/refund/RefundManager.kt b/wallet/src/main/java/net/taler/wallet/refund/RefundManager.kt
index 9c292aa..f3c41e8 100644
--- a/wallet/src/main/java/net/taler/wallet/refund/RefundManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/refund/RefundManager.kt
@@ -21,7 +21,7 @@ import androidx.lifecycle.MutableLiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
-import net.taler.lib.common.Amount
+import net.taler.common.Amount
import net.taler.wallet.backend.WalletBackendApi
sealed class RefundStatus {
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
index 866b363..128ca04 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
@@ -25,8 +25,8 @@ import android.view.MenuItem
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
+import net.taler.common.Amount
import net.taler.common.startActivitySafe
-import net.taler.lib.common.Amount
import net.taler.wallet.MainViewModel
import net.taler.wallet.R
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
index 50181c5..cca370e 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
@@ -23,10 +23,10 @@ import androidx.annotation.StringRes
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
+import net.taler.common.Amount
import net.taler.common.ContractMerchant
import net.taler.common.ContractProduct
-import net.taler.lib.common.Amount
-import net.taler.lib.common.Timestamp
+import net.taler.common.Timestamp
import net.taler.wallet.R
import net.taler.wallet.backend.TalerErrorInfo
import net.taler.wallet.cleanExchange
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt
index 90510e6..e63d451 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt
@@ -36,6 +36,7 @@ import androidx.recyclerview.selection.SelectionTracker
import androidx.recyclerview.selection.StorageStrategy
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL
+import net.taler.common.Amount
import net.taler.common.fadeIn
import net.taler.common.fadeOut
import net.taler.wallet.MainViewModel
@@ -112,12 +113,12 @@ class TransactionsFragment : Fragment(), OnTransactionClickListener, ActionMode.
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
- model.balances.observe(viewLifecycleOwner, { balances ->
- balances.find { it.currency == currency }?.available?.let { amount ->
+ model.balances.observe(viewLifecycleOwner) { balances ->
+ balances.find { it.currency == currency }?.available?.let { amount: Amount ->
requireActivity().title =
getString(R.string.transactions_detail_title_balance, amount)
}
- })
+ }
}
override fun onSaveInstanceState(outState: Bundle) {
diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/ManualWithdrawFragment.kt b/wallet/src/main/java/net/taler/wallet/withdraw/ManualWithdrawFragment.kt
index e78ff44..0cb39d2 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/ManualWithdrawFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/withdraw/ManualWithdrawFragment.kt
@@ -24,8 +24,8 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
+import net.taler.common.Amount
import net.taler.common.hideKeyboard
-import net.taler.lib.common.Amount
import net.taler.wallet.MainViewModel
import net.taler.wallet.R
import net.taler.wallet.databinding.FragmentManualWithdrawBinding
diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/ManualWithdrawSuccessFragment.kt b/wallet/src/main/java/net/taler/wallet/withdraw/ManualWithdrawSuccessFragment.kt
index 4ea3e73..cdacde6 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/ManualWithdrawSuccessFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/withdraw/ManualWithdrawSuccessFragment.kt
@@ -60,8 +60,8 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import com.google.android.material.composethemeadapter.MdcTheme
+import net.taler.common.Amount
import net.taler.common.startActivitySafe
-import net.taler.lib.common.Amount
import net.taler.wallet.MainViewModel
import net.taler.wallet.R
diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt b/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt
index 08cbc2e..dfae920 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt
@@ -25,10 +25,10 @@ import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
+import net.taler.common.Amount
import net.taler.common.EventObserver
import net.taler.common.fadeIn
import net.taler.common.fadeOut
-import net.taler.lib.common.Amount
import net.taler.wallet.MainViewModel
import net.taler.wallet.R
import net.taler.wallet.cleanExchange
diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt b/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
index fbb5c18..d96f421 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
@@ -24,9 +24,9 @@ import androidx.lifecycle.MutableLiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
+import net.taler.common.Amount
import net.taler.common.Event
import net.taler.common.toEvent
-import net.taler.lib.common.Amount
import net.taler.wallet.TAG
import net.taler.wallet.backend.TalerErrorInfo
import net.taler.wallet.backend.WalletBackendApi