gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-android] 02/04: -remove multiplatform dependency, library u


From: gnunet
Subject: [taler-taler-android] 02/04: -remove multiplatform dependency, library upgrade WIP
Date: Wed, 01 Jun 2022 13:32:23 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository taler-android.

commit 023ea96d26a65d29d408ef78c9405411bb842afd
Author: Florian Dold <florian@dold.me>
AuthorDate: Mon May 16 19:58:31 2022 +0200

    -remove multiplatform dependency, library upgrade WIP
---
 .gitmodules                                        |   3 -
 .../ui/authentication/AuthenticationFragment.kt    |  44 ++--
 .../ui/authentication/SecurityQuestionFragment.kt  |  27 ++-
 .../gnu/anastasis/ui/authentication/SmsFragment.kt |  23 ++-
 .../anastasis/ui/authentication/VideoFragment.kt   |  34 +++-
 .../ui/identity/ChangeLocationFragment.kt          |  16 +-
 build.gradle                                       |   1 +
 cashier/build.gradle                               |   3 +
 .../java/net/taler/cashier/AboutDialogFragment.kt  |   2 +-
 .../main/java/net/taler/cashier/BalanceFragment.kt |   2 +-
 .../main/java/net/taler/cashier/MainViewModel.kt   |  17 +-
 .../src/main/java/net/taler/cashier/Response.kt    |   5 +-
 .../main/java/net/taler/cashier/SignedAmount.kt    |   2 +-
 .../java/net/taler/cashier/config/ConfigManager.kt |   4 +-
 .../net/taler/cashier/withdraw/WithdrawManager.kt  |   2 +-
 merchant-lib/build.gradle                          |   4 +-
 .../main/java/net/taler/merchantlib/MerchantApi.kt |  27 +--
 .../java/net/taler/merchantlib/OrderHistory.kt     |   4 +-
 .../src/main/java/net/taler/merchantlib/Orders.kt  |   2 +-
 .../src/main/java/net/taler/merchantlib/Refunds.kt |   3 +-
 .../main/java/net/taler/merchantlib/Response.kt    |  10 +-
 .../java/net/taler/merchantlib/MerchantApiTest.kt  |   4 +-
 .../java/net/taler/merchantlib/MockHttpClient.kt   |  12 --
 merchant-terminal/src/main/AndroidManifest.xml     |   1 +
 .../net/taler/merchantpos/config/ConfigManager.kt  |   7 +-
 .../java/net/taler/merchantpos/config/PosConfig.kt |   2 +-
 .../java/net/taler/merchantpos/order/LiveOrder.kt  |   2 +-
 .../main/java/net/taler/merchantpos/order/Order.kt |   4 +-
 .../taler/merchantpos/order/OrderStateFragment.kt  |   9 +-
 .../taler/merchantpos/payment/PaymentManager.kt    |   2 +-
 .../net/taler/merchantpos/refund/RefundFragment.kt |   4 +-
 .../net/taler/merchantpos/refund/RefundManager.kt  |   2 +-
 .../taler/merchantpos/order/OrderManagerTest.kt    |   2 +-
 multiplatform                                      |   1 -
 taler-kotlin-android/build.gradle                  |   2 -
 taler-kotlin-android/src/main/AndroidManifest.xml  |   3 -
 .../src/main/java/net/taler/common/Amount.kt       | 199 +++++++++++++++++++
 .../src/main/java/net/taler/common/AndroidUtils.kt |   1 -
 .../main/java/net/taler/common/ContractTerms.kt    |   2 -
 .../src/main/java/net/taler/common/TalerUri.kt     |  62 ++++++
 .../src/main/java/net/taler/common/Time.kt         | 129 ++++++++++++
 .../src/main/java/net/taler/common/Version.kt      |  70 +++++++
 .../src/test/java/net/taler/common/AmountTest.kt   | 221 +++++++++++++++++++++
 .../java/net/taler/common/ContractTermsTest.kt     |   1 -
 .../src/test/java/net/taler/common/TalerUriTest.kt |  65 ++++++
 .../src/test/java/net/taler/common/TestUtils.kt    |  28 ++-
 .../src/test/java/net/taler/common/TimeTest.kt     |  46 +++++
 .../src/test/java/net/taler/common/VersionTest.kt  |  65 ++++++
 wallet/build.gradle                                |   4 +-
 wallet/src/main/AndroidManifest.xml                |   1 +
 .../net/taler/wallet/balances/BalanceAdapter.kt    |   2 +-
 .../net/taler/wallet/exchanges/ExchangeFees.kt     |   5 +-
 .../taler/wallet/exchanges/ExchangeFeesFragment.kt |   2 +-
 .../net/taler/wallet/payment/PaymentManager.kt     |   2 +-
 .../net/taler/wallet/payment/PaymentResponses.kt   |   2 +-
 .../taler/wallet/payment/PromptPaymentFragment.kt  |   6 +-
 .../java/net/taler/wallet/refund/RefundManager.kt  |   2 +-
 .../transactions/TransactionDetailFragment.kt      |   2 +-
 .../net/taler/wallet/transactions/Transactions.kt  |   4 +-
 .../wallet/transactions/TransactionsFragment.kt    |   7 +-
 .../wallet/withdraw/ManualWithdrawFragment.kt      |   2 +-
 .../withdraw/ManualWithdrawSuccessFragment.kt      |   2 +-
 .../wallet/withdraw/PromptWithdrawFragment.kt      |   2 +-
 .../net/taler/wallet/withdraw/WithdrawManager.kt   |   2 +-
 64 files changed, 1057 insertions(+), 171 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
index cb92566..0000000
--- a/multiplatform
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit cb92566166884efdaad5e93ef25e2de1f3034616
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/cashier/src/main/java/net/taler/cashier/SignedAmount.kt 
b/taler-kotlin-android/src/test/java/net/taler/common/TestUtils.kt
similarity index 54%
copy from cashier/src/main/java/net/taler/cashier/SignedAmount.kt
copy to taler-kotlin-android/src/test/java/net/taler/common/TestUtils.kt
index e79acfd..b0f191d 100644
--- a/cashier/src/main/java/net/taler/cashier/SignedAmount.kt
+++ b/taler-kotlin-android/src/test/java/net/taler/common/TestUtils.kt
@@ -14,17 +14,27 @@
  * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-package net.taler.cashier
+package net.taler.common
 
-import net.taler.lib.common.Amount
+import kotlin.random.Random
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
 
-data class SignedAmount(
-    val positive: Boolean,
-    val amount: Amount
-) {
+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("")
 
-    override fun toString(): String {
-        return if (positive) "$amount" else "-$amount"
+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

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]