[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-taler-android] branch master updated (8eb241c -> 3ceaeb3)
From: |
gnunet |
Subject: |
[taler-taler-android] branch master updated (8eb241c -> 3ceaeb3) |
Date: |
Thu, 30 Jul 2020 22:12:20 +0200 |
This is an automated email from the git hooks/post-receive script.
torsten-grote pushed a change to branch master
in repository taler-android.
from 8eb241c [pos] refactor configuration fetching and validation
new bc35e89 [pos] adapt history to new v1 API
new e19ba09 [wallet] update to new wallet-core with v8 exchange API
new 8815105 Split out common code into multiplatform Kotlin library
new 3ceaeb3 [wallet] upgrade payment flow to new API
The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.gitignore | 1 +
.gitlab-ci.yml | 1 +
.idea/gradle.xml | 1 +
anastasis-ui/build.gradle | 2 +-
build.gradle | 4 -
cashier/.gitlab-ci.yml | 1 +
cashier/build.gradle | 2 +-
.../main/java/net/taler/cashier/MainViewModel.kt | 16 +-
merchant-lib/.gitlab-ci.yml | 1 +
merchant-lib/build.gradle | 2 +-
.../main/java/net/taler/merchantlib/MerchantApi.kt | 19 +-
.../java/net/taler/merchantlib/MerchantConfig.kt | 2 +-
.../{MerchantConfig.kt => OrderHistory.kt} | 40 ++--
.../java/net/taler/merchantlib/PostOrderRequest.kt | 6 +-
.../main/java/net/taler/merchantlib/Response.kt | 6 +-
.../java/net/taler/merchantlib/MerchantApiTest.kt | 1 +
merchant-terminal/.gitlab-ci.yml | 1 +
merchant-terminal/build.gradle | 3 -
.../java/net/taler/merchantpos/MainViewModel.kt | 8 +-
.../taler/merchantpos/history/HistoryManager.kt | 62 ++----
.../merchantpos/history/MerchantHistoryFragment.kt | 34 +--
.../taler/merchantpos/history/RefundFragment.kt | 3 +-
.../net/taler/merchantpos/history/RefundManager.kt | 11 +-
settings.gradle | 3 +
{cashier => taler-kotlin-android}/.gitignore | 0
taler-kotlin-android/.gitlab-ci.yml | 12 ++
.../build.gradle | 6 +
.../consumer-rules.pro | 0
.../proguard-rules.pro | 0
.../src/main/AndroidManifest.xml | 0
.../src/main/java/net/taler/common/AmountMixin.kt | 51 +++++
.../src/main/java/net/taler/common/AndroidUtils.kt | 0
.../main/java/net/taler/common/ByteArrayUtils.kt | 0
.../main/java/net/taler/common/CombinedLiveData.kt | 0
.../main/java/net/taler/common/ContractTerms.kt | 6 +-
.../src/main/java/net/taler/common/Event.kt | 0
.../src/main/java/net/taler/common/NfcManager.kt | 0
.../main/java/net/taler/common/QrCodeManager.kt | 0
.../src/main/java/net/taler/common/SignedAmount.kt | 0
.../src/main/java/net/taler/common/TalerUtils.kt | 3 +-
.../main/res/drawable/selectable_background.xml | 0
.../src/main/res/values-night/colors.xml | 0
.../src/main/res/values/colors.xml | 0
.../src/main/res/values/strings.xml | 0
.../java/net/taler/common/ContractTermsTest.kt | 74 +++++++
taler-kotlin-common/.gitlab-ci.yml | 6 +-
taler-kotlin-common/build.gradle | 130 ++++++------
.../kotlin}/net/taler/common/Amount.kt | 70 +------
.../src/commonMain/kotlin/net/taler/common/Time.kt | 81 ++++++++
.../kotlin}/net/taler/common/Version.kt | 0
.../kotlin}/net/taler/common/AmountTest.kt | 147 ++++---------
.../kotlin/net/taler/common/TestUtils.kt | 19 +-
.../kotlin}/net/taler/common/VersionTest.kt | 6 +-
.../src/jsMain/kotlin/net/taler/common/Time.kt | 16 +-
.../src/jvmMain/kotlin/net/taler/common/Time.kt | 16 +-
.../src/nativeMain/kotlin/net/taler/common/Time.kt | 16 +-
wallet/.gitlab-ci.yml | 1 +
wallet/build.gradle | 10 +-
.../src/main/java/net/taler/wallet/MainActivity.kt | 5 +-
.../src/main/java/net/taler/wallet/MainFragment.kt | 2 +-
.../main/java/net/taler/wallet/MainViewModel.kt | 42 ++--
.../net/taler/wallet/backend/WalletBackendApi.kt | 4 +-
.../taler/wallet/backend/WalletBackendService.kt | 9 +-
.../net/taler/wallet/balances/BalanceAdapter.kt | 11 +-
.../net/taler/wallet/balances/BalancesFragment.kt | 2 +-
.../main/java/net/taler/wallet/crypto/Encoding.kt | 134 ------------
.../net/taler/wallet/exchanges/ExchangeFees.kt | 38 ++--
.../net/taler/wallet/exchanges/ExchangeManager.kt | 3 +-
.../SelectExchangeFragment.kt | 12 +-
.../net/taler/wallet/history/DevHistoryAdapter.kt | 110 ----------
.../net/taler/wallet/history/DevHistoryFragment.kt | 87 --------
.../net/taler/wallet/history/DevHistoryManager.kt | 75 -------
.../java/net/taler/wallet/history/HistoryEvent.kt | 199 ------------------
.../net/taler/wallet/history/JsonDialogFragment.kt | 57 ------
.../net/taler/wallet/payment/PaymentManager.kt | 108 +++++-----
.../net/taler/wallet/payment/PaymentResponses.kt | 61 ++++++
.../taler/wallet/payment/PromptPaymentFragment.kt | 7 +-
.../wallet/pending/PendingOperationsManager.kt | 4 +-
.../wallet/transactions/TransactionManager.kt | 8 -
.../wallet/transactions/TransactionsFragment.kt | 2 +-
.../java/net/taler/wallet/withdraw/ExchangeFees.kt | 95 ---------
.../wallet/withdraw/ManualWithdrawFragment.kt | 2 +-
.../wallet/withdraw/PromptWithdrawFragment.kt | 26 ++-
.../wallet/withdraw/ReviewExchangeTosFragment.kt | 2 +-
.../net/taler/wallet/withdraw/WithdrawManager.kt | 227 ++++++++++-----------
wallet/src/main/res/drawable/ic_directions.xml | 25 ---
.../src/main/res/drawable/ic_edit.xml | 4 +-
wallet/src/main/res/drawable/ic_history.xml | 9 -
.../res/drawable/transaction_payment_aborted.xml | 25 ---
.../main/res/drawable/transaction_tip_declined.xml | 25 ---
wallet/src/main/res/layout/fragment_json.xml | 40 ----
.../main/res/layout/fragment_prompt_withdraw.xml | 6 +-
.../src/main/res/layout/fragment_transactions.xml | 2 +-
wallet/src/main/res/layout/list_item_history.xml | 75 -------
wallet/src/main/res/layout/payment_bottom_bar.xml | 1 -
wallet/src/main/res/menu/activity_main_drawer.xml | 4 -
wallet/src/main/res/navigation/nav_graph.xml | 12 +-
wallet/src/main/res/values/strings.xml | 1 -
wallet/src/main/res/xml/settings_backup.xml | 2 +-
.../net/taler/wallet/crypto/Base32CrockfordTest.kt | 36 ----
100 files changed, 807 insertions(+), 1692 deletions(-)
copy merchant-lib/src/main/java/net/taler/merchantlib/{MerchantConfig.kt =>
OrderHistory.kt} (59%)
copy {cashier => taler-kotlin-android}/.gitignore (100%)
create mode 100644 taler-kotlin-android/.gitlab-ci.yml
copy {taler-kotlin-common => taler-kotlin-android}/build.gradle (95%)
rename {taler-kotlin-common => taler-kotlin-android}/consumer-rules.pro (100%)
rename {taler-kotlin-common => taler-kotlin-android}/proguard-rules.pro (100%)
rename {taler-kotlin-common =>
taler-kotlin-android}/src/main/AndroidManifest.xml (100%)
create mode 100644
taler-kotlin-android/src/main/java/net/taler/common/AmountMixin.kt
rename {taler-kotlin-common =>
taler-kotlin-android}/src/main/java/net/taler/common/AndroidUtils.kt (100%)
rename {taler-kotlin-common =>
taler-kotlin-android}/src/main/java/net/taler/common/ByteArrayUtils.kt (100%)
rename {taler-kotlin-common =>
taler-kotlin-android}/src/main/java/net/taler/common/CombinedLiveData.kt (100%)
rename {taler-kotlin-common =>
taler-kotlin-android}/src/main/java/net/taler/common/ContractTerms.kt (95%)
rename {taler-kotlin-common =>
taler-kotlin-android}/src/main/java/net/taler/common/Event.kt (100%)
rename {taler-kotlin-common =>
taler-kotlin-android}/src/main/java/net/taler/common/NfcManager.kt (100%)
rename {taler-kotlin-common =>
taler-kotlin-android}/src/main/java/net/taler/common/QrCodeManager.kt (100%)
rename {taler-kotlin-common =>
taler-kotlin-android}/src/main/java/net/taler/common/SignedAmount.kt (100%)
rename {taler-kotlin-common =>
taler-kotlin-android}/src/main/java/net/taler/common/TalerUtils.kt (97%)
rename {taler-kotlin-common =>
taler-kotlin-android}/src/main/res/drawable/selectable_background.xml (100%)
rename {taler-kotlin-common =>
taler-kotlin-android}/src/main/res/values-night/colors.xml (100%)
rename {taler-kotlin-common =>
taler-kotlin-android}/src/main/res/values/colors.xml (100%)
rename {taler-kotlin-common =>
taler-kotlin-android}/src/main/res/values/strings.xml (100%)
create mode 100644
taler-kotlin-android/src/test/java/net/taler/common/ContractTermsTest.kt
rename taler-kotlin-common/src/{main/java =>
commonMain/kotlin}/net/taler/common/Amount.kt (70%)
create mode 100644
taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Time.kt
rename taler-kotlin-common/src/{main/java =>
commonMain/kotlin}/net/taler/common/Version.kt (100%)
rename taler-kotlin-common/src/{test/java =>
commonTest/kotlin}/net/taler/common/AmountTest.kt (65%)
copy merchant-terminal/src/main/java/net/taler/merchantpos/payment/Payment.kt
=> taler-kotlin-common/src/commonTest/kotlin/net/taler/common/TestUtils.kt (67%)
rename taler-kotlin-common/src/{test/java =>
commonTest/kotlin}/net/taler/common/VersionTest.kt (95%)
copy merchant-terminal/src/main/java/net/taler/merchantpos/payment/Payment.kt
=> taler-kotlin-common/src/jsMain/kotlin/net/taler/common/Time.kt (70%)
copy merchant-terminal/src/main/java/net/taler/merchantpos/payment/Payment.kt
=> taler-kotlin-common/src/jvmMain/kotlin/net/taler/common/Time.kt (70%)
copy merchant-terminal/src/main/java/net/taler/merchantpos/payment/Payment.kt
=> taler-kotlin-common/src/nativeMain/kotlin/net/taler/common/Time.kt (70%)
delete mode 100644 wallet/src/main/java/net/taler/wallet/crypto/Encoding.kt
copy merchant-lib/src/main/java/net/taler/merchantlib/ConfigResponse.kt =>
wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFees.kt (54%)
rename wallet/src/main/java/net/taler/wallet/{withdraw =>
exchanges}/SelectExchangeFragment.kt (94%)
delete mode 100644
wallet/src/main/java/net/taler/wallet/history/DevHistoryAdapter.kt
delete mode 100644
wallet/src/main/java/net/taler/wallet/history/DevHistoryFragment.kt
delete mode 100644
wallet/src/main/java/net/taler/wallet/history/DevHistoryManager.kt
delete mode 100644
wallet/src/main/java/net/taler/wallet/history/HistoryEvent.kt
delete mode 100644
wallet/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt
create mode 100644
wallet/src/main/java/net/taler/wallet/payment/PaymentResponses.kt
delete mode 100644
wallet/src/main/java/net/taler/wallet/withdraw/ExchangeFees.kt
delete mode 100644 wallet/src/main/res/drawable/ic_directions.xml
copy anastasis-ui/src/main/res/drawable/ic_baseline_check.xml =>
wallet/src/main/res/drawable/ic_edit.xml (51%)
delete mode 100644 wallet/src/main/res/drawable/ic_history.xml
delete mode 100644 wallet/src/main/res/drawable/transaction_payment_aborted.xml
delete mode 100644 wallet/src/main/res/drawable/transaction_tip_declined.xml
delete mode 100644 wallet/src/main/res/layout/fragment_json.xml
delete mode 100644 wallet/src/main/res/layout/list_item_history.xml
delete mode 100644
wallet/src/test/java/net/taler/wallet/crypto/Base32CrockfordTest.kt
diff --git a/.gitignore b/.gitignore
index 7e4952a..caf9ce6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
*.iml
.gradle
/local.properties
+/.idea/artifacts
/.idea/caches
/.idea/libraries
/.idea/misc.xml
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 48f1aec..6dc4426 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -14,6 +14,7 @@ include:
- local: 'merchant-lib/.gitlab-ci.yml'
- local: 'merchant-terminal/.gitlab-ci.yml'
- local: 'taler-kotlin-common/.gitlab-ci.yml'
+ - local: 'taler-kotlin-android/.gitlab-ci.yml'
- local: 'wallet/.gitlab-ci.yml'
after_script:
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 581abbf..01ed15f 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -14,6 +14,7 @@
<option value="$PROJECT_DIR$/cashier" />
<option value="$PROJECT_DIR$/merchant-lib" />
<option value="$PROJECT_DIR$/merchant-terminal" />
+ <option value="$PROJECT_DIR$/taler-kotlin-android" />
<option value="$PROJECT_DIR$/taler-kotlin-common" />
<option value="$PROJECT_DIR$/wallet" />
</set>
diff --git a/anastasis-ui/build.gradle b/anastasis-ui/build.gradle
index 0391c7c..ff0eec5 100644
--- a/anastasis-ui/build.gradle
+++ b/anastasis-ui/build.gradle
@@ -51,7 +51,7 @@ android {
}
dependencies {
- implementation project(":taler-kotlin-common")
+ implementation project(":taler-kotlin-android")
implementation 'com.google.android.material:material:1.2.0-beta01'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
diff --git a/build.gradle b/build.gradle
index 76f687e..442d232 100644
--- a/build.gradle
+++ b/build.gradle
@@ -24,7 +24,3 @@ allprojects {
maven { url 'https://jitpack.io' }
}
}
-
-task clean(type: Delete) {
- delete rootProject.buildDir
-}
diff --git a/cashier/.gitlab-ci.yml b/cashier/.gitlab-ci.yml
index 6a7baed..6b73dee 100644
--- a/cashier/.gitlab-ci.yml
+++ b/cashier/.gitlab-ci.yml
@@ -6,6 +6,7 @@ cashier_test:
changes:
- cashier/**/*
- taler-kotlin-common/**/*
+ - taler-kotlin-android/**/*
- build.gradle
script: ./gradlew :cashier:check :cashier:assembleRelease
artifacts:
diff --git a/cashier/build.gradle b/cashier/build.gradle
index 0d06c60..641a039 100644
--- a/cashier/build.gradle
+++ b/cashier/build.gradle
@@ -54,7 +54,7 @@ android {
}
dependencies {
- implementation project(":taler-kotlin-common")
+ implementation project(":taler-kotlin-android")
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.security:security-crypto:1.0.0-rc02'
implementation 'com.google.android.material:material:1.1.0'
diff --git a/cashier/src/main/java/net/taler/cashier/MainViewModel.kt
b/cashier/src/main/java/net/taler/cashier/MainViewModel.kt
index c8d9a3b..a4fd35e 100644
--- a/cashier/src/main/java/net/taler/cashier/MainViewModel.kt
+++ b/cashier/src/main/java/net/taler/cashier/MainViewModel.kt
@@ -40,6 +40,7 @@ import net.taler.common.isOnline
private val TAG = MainViewModel::class.java.simpleName
+private const val VERSION_BANK = "0:0:0"
private const val PREF_NAME = "net.taler.cashier.prefs"
private const val PREF_KEY_BANK_URL = "bankUrl"
private const val PREF_KEY_USERNAME = "username"
@@ -86,20 +87,21 @@ class MainViewModel(private val app: Application) :
AndroidViewModel(app) {
fun checkAndSaveConfig(config: Config) {
mConfigResult.value = null
viewModelScope.launch(Dispatchers.IO) {
- val url = "${config.bankUrl}/accounts/${config.username}/balance"
+ val url = "${config.bankUrl}/config"
Log.d(TAG, "Checking config: $url")
val result = when (val response = makeJsonGetRequest(url, config))
{
is HttpJsonResult.Success -> {
- val balance = response.json.getString("balance")
+ val version = response.json.getString("version")
+ // TODO check if version is compatible
+ val currency = response.json.getString("currency")
try {
- val amount = SignedAmount.fromJSONString(balance)
- mCurrency.postValue(amount.amount.currency)
- prefs.edit().putString(PREF_KEY_CURRENCY,
amount.amount.currency).apply()
+ mCurrency.postValue(currency)
+ prefs.edit().putString(PREF_KEY_CURRENCY,
currency).apply()
// save config
saveConfig(config)
ConfigResult.Success
- } catch (e: AmountParserException) {
- ConfigResult.Error(false, "Invalid Amount: $balance")
+ } catch (e: Exception) {
+ ConfigResult.Error(false, "Invalid Config:
${response.json}")
}
}
is HttpJsonResult.Error -> {
diff --git a/merchant-lib/.gitlab-ci.yml b/merchant-lib/.gitlab-ci.yml
index 62a7516..8f7c7c2 100644
--- a/merchant-lib/.gitlab-ci.yml
+++ b/merchant-lib/.gitlab-ci.yml
@@ -2,6 +2,7 @@ merchant_lib_test:
stage: test
only:
changes:
+ - taler-kotlin-common/**/*
- merchant-lib/**/*
- build.gradle
script: ./gradlew :merchant-lib:check
diff --git a/merchant-lib/build.gradle b/merchant-lib/build.gradle
index 128f4c1..33e8379 100644
--- a/merchant-lib/build.gradle
+++ b/merchant-lib/build.gradle
@@ -45,7 +45,7 @@ android {
}
dependencies {
- api project(":taler-kotlin-common")
+ api project(":taler-kotlin-android")
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
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 e995724..db37586 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt
@@ -16,7 +16,6 @@
package net.taler.merchantlib
-import android.util.Log
import io.ktor.client.HttpClient
import io.ktor.client.engine.okhttp.OkHttp
import io.ktor.client.features.json.JsonFeature
@@ -25,8 +24,6 @@ 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.statement.HttpResponse
-import io.ktor.client.statement.readBytes
import io.ktor.http.ContentType.Application.Json
import io.ktor.http.HttpHeaders.Authorization
import io.ktor.http.contentType
@@ -64,14 +61,16 @@ class MerchantApi(private val httpClient: HttpClient) {
suspend fun deleteOrder(
merchantConfig: MerchantConfig,
orderId: String
- ): Response<HttpResponse> = response {
- val resp =
httpClient.delete(merchantConfig.urlFor("private/orders/$orderId")) {
+ ): Response<Unit> = response {
+ httpClient.delete(merchantConfig.urlFor("private/orders/$orderId")) {
header(Authorization, "ApiKey ${merchantConfig.apiKey}")
- } as HttpResponse
- // TODO remove when the API call was fixed
- Log.e("TEST", "status: ${resp.status.value}")
- Log.e("TEST", String(resp.readBytes()))
- resp
+ } as Unit
+ }
+
+ suspend fun getOrderHistory(merchantConfig: MerchantConfig):
Response<OrderHistory> = response {
+ httpClient.get(merchantConfig.urlFor("private/orders")) {
+ header(Authorization, "ApiKey ${merchantConfig.apiKey}")
+ } as OrderHistory
}
}
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantConfig.kt
b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantConfig.kt
index a01624e..a8d113e 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantConfig.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantConfig.kt
@@ -31,7 +31,7 @@ data class MerchantConfig(
fun urlFor(endpoint: String): String {
val sb = StringBuilder(baseUrl)
if (sb.last() != '/') sb.append('/')
- sb.append("instances/$instance/")
+ instance?.let { sb.append("instances/$it/") }
sb.append(endpoint)
return sb.toString()
}
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantConfig.kt
b/merchant-lib/src/main/java/net/taler/merchantlib/OrderHistory.kt
similarity index 59%
copy from merchant-lib/src/main/java/net/taler/merchantlib/MerchantConfig.kt
copy to merchant-lib/src/main/java/net/taler/merchantlib/OrderHistory.kt
index a01624e..718bde5 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantConfig.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/OrderHistory.kt
@@ -18,21 +18,29 @@ package net.taler.merchantlib
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
+import net.taler.common.Amount
+import net.taler.common.Timestamp
@Serializable
-data class MerchantConfig(
- @SerialName("base_url")
- val baseUrl: String,
- // TODO remove instance when it is part of baseURL
- val instance: String? = null,
- @SerialName("api_key")
- val apiKey: String
-) {
- fun urlFor(endpoint: String): String {
- val sb = StringBuilder(baseUrl)
- if (sb.last() != '/') sb.append('/')
- sb.append("instances/$instance/")
- sb.append(endpoint)
- return sb.toString()
- }
-}
+data class OrderHistory(
+ val orders: List<OrderHistoryEntry>
+)
+
+@Serializable
+data class OrderHistoryEntry(
+ // order ID of the transaction related to this entry.
+ @SerialName("order_id")
+ val orderId: String,
+
+ // when the order was created
+ val timestamp: Timestamp,
+
+ // the amount of money the order is for
+ val amount: Amount,
+
+ // the summary of the order
+ val summary: String,
+
+ // whether some part of the order is refundable
+ val refundable: Boolean
+)
diff --git
a/merchant-lib/src/main/java/net/taler/merchantlib/PostOrderRequest.kt
b/merchant-lib/src/main/java/net/taler/merchantlib/PostOrderRequest.kt
index a6e74d6..4854a80 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/PostOrderRequest.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/PostOrderRequest.kt
@@ -48,11 +48,11 @@ sealed class CheckPaymentResponse {
override fun deserialize(decoder: Decoder): CheckPaymentResponse {
val input = decoder as JsonInput
val tree = input.decodeJson() as JsonObject
- val paid = tree.getPrimitive("paid").boolean
-// return if (paid) decoder.json.fromJson(Paid.serializer(), tree)
+ val orderStatus = tree.getPrimitive("order_status").content
+// return if (orderStatus == "paid")
decoder.json.fromJson(Paid.serializer(), tree)
// else decoder.json.fromJson(Unpaid.serializer(), tree)
// manual parsing due to
https://github.com/Kotlin/kotlinx.serialization/issues/576
- return if (paid) Paid(
+ return if (orderStatus == "paid") Paid(
refunded = tree.getPrimitive("refunded").boolean
) else Unpaid(
talerPayUri = tree.getPrimitive("taler_pay_uri").content
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 1b49d78..65a12a9 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/Response.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/Response.kt
@@ -18,6 +18,8 @@ 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 kotlinx.serialization.Serializable
class Response<out T> private constructor(
@@ -29,6 +31,7 @@ class Response<out T> private constructor(
return try {
success(request())
} catch (e: Throwable) {
+ println(e)
failure(e)
}
}
@@ -63,10 +66,11 @@ class Response<out T> private constructor(
private suspend fun getFailureString(failure: Failure): String = when
(failure.exception) {
is ClientRequestException -> getExceptionString(failure.exception)
+ is ServerResponseException -> getExceptionString(failure.exception)
else -> failure.exception.toString()
}
- private suspend fun getExceptionString(e: ClientRequestException): String {
+ private suspend fun getExceptionString(e: ResponseException): String {
return try {
val error: Error = e.response.receive()
"Error ${error.code}: ${error.hint}"
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 ea5a12a..deed81e 100644
--- a/merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt
+++ b/merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt
@@ -113,6 +113,7 @@ class MerchantApiTest {
val unpaidResponse = CheckPaymentResponse.Unpaid(false,
"http://taler.net/foo")
httpClient.giveJsonResponse("http://example.net/instances/testInstance/private/orders/$orderId")
{
"""{
+ "order_status": "unpaid",
"paid": ${unpaidResponse.paid},
"taler_pay_uri": "${unpaidResponse.talerPayUri}"
}""".trimIndent()
diff --git a/merchant-terminal/.gitlab-ci.yml b/merchant-terminal/.gitlab-ci.yml
index 74ac21f..d159902 100644
--- a/merchant-terminal/.gitlab-ci.yml
+++ b/merchant-terminal/.gitlab-ci.yml
@@ -5,6 +5,7 @@ merchant_test:
- merchant-terminal/**/*
- merchant-lib/**/*
- taler-kotlin-common/**/*
+ - taler-kotlin-android/**/*
- build.gradle
script: ./gradlew :merchant-terminal:check :merchant-terminal:assembleRelease
artifacts:
diff --git a/merchant-terminal/build.gradle b/merchant-terminal/build.gradle
index 1cec0c5..4499892 100644
--- a/merchant-terminal/build.gradle
+++ b/merchant-terminal/build.gradle
@@ -73,9 +73,6 @@ dependencies {
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
- // JSON parsing and serialization
- implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.10.2"
-
testImplementation 'androidx.test.ext:junit:1.1.1'
testImplementation 'org.robolectric:robolectric:4.3.1'
}
diff --git
a/merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt
b/merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt
index b62c550..905738b 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt
@@ -20,9 +20,6 @@ import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.android.volley.toolbox.Volley
-import
com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.module.kotlin.KotlinModule
import net.taler.merchantlib.MerchantApi
import net.taler.merchantlib.getDefaultHttpClient
import net.taler.merchantpos.config.ConfigManager
@@ -35,9 +32,6 @@ class MainViewModel(app: Application) : AndroidViewModel(app)
{
private val httpClient = getDefaultHttpClient()
private val api = MerchantApi(httpClient)
- private val mapper = ObjectMapper()
- .registerModule(KotlinModule())
- .configure(FAIL_ON_UNKNOWN_PROPERTIES, false)
private val queue = Volley.newRequestQueue(app)
val orderManager = OrderManager(app)
@@ -45,7 +39,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app)
{
addConfigurationReceiver(orderManager)
}
val paymentManager = PaymentManager(app, configManager, viewModelScope,
api)
- val historyManager = HistoryManager(configManager, queue, mapper)
+ val historyManager = HistoryManager(configManager, viewModelScope, api)
val refundManager = RefundManager(configManager, queue)
override fun onCleared() {
diff --git
a/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryManager.kt
b/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryManager.kt
index 24c7a0c..cb77096 100644
---
a/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryManager.kt
+++
b/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryManager.kt
@@ -19,40 +19,22 @@ package net.taler.merchantpos.history
import androidx.annotation.UiThread
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
-import com.android.volley.Request.Method.GET
-import com.android.volley.RequestQueue
-import com.android.volley.Response.Listener
-import com.fasterxml.jackson.annotation.JsonIgnore
-import com.fasterxml.jackson.annotation.JsonProperty
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.module.kotlin.readValue
-import net.taler.common.Amount
-import net.taler.common.Timestamp
-import net.taler.merchantpos.LogErrorListener
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import net.taler.merchantlib.MerchantApi
+import net.taler.merchantlib.OrderHistoryEntry
import net.taler.merchantpos.config.ConfigManager
-import net.taler.merchantpos.config.MerchantRequest
-import org.json.JSONObject
-
-data class HistoryItem(
- @JsonProperty("order_id")
- val orderId: String,
- val amount: Amount,
- val summary: String,
- val timestamp: Timestamp
-) {
- @get:JsonIgnore
- val time = timestamp.ms
-}
sealed class HistoryResult {
- object Error : HistoryResult()
- class Success(val items: List<HistoryItem>) : HistoryResult()
+ class Error(val msg: String) : HistoryResult()
+ class Success(val items: List<OrderHistoryEntry>) : HistoryResult()
}
class HistoryManager(
private val configManager: ConfigManager,
- private val queue: RequestQueue,
- private val mapper: ObjectMapper
+ private val scope: CoroutineScope,
+ private val api: MerchantApi
) {
private val mIsLoading = MutableLiveData(false)
@@ -65,29 +47,17 @@ class HistoryManager(
internal fun fetchHistory() {
mIsLoading.value = true
val merchantConfig = configManager.merchantConfig!!
- val params = mapOf("instance" to merchantConfig.instance!!)
- val req = MerchantRequest(GET, merchantConfig, "history", params, null,
- Listener { onHistoryResponse(it) },
- LogErrorListener { onHistoryError() })
- queue.add(req)
- }
-
- @UiThread
- private fun onHistoryResponse(body: JSONObject) {
- mIsLoading.value = false
- val items = arrayListOf<HistoryItem>()
- val historyJson = body.getJSONArray("history")
- for (i in 0 until historyJson.length()) {
- val historyItem: HistoryItem =
mapper.readValue(historyJson.getString(i))
- items.add(historyItem)
+ scope.launch(Dispatchers.IO) {
+ api.getOrderHistory(merchantConfig).handle(::onHistoryError) {
+ mIsLoading.postValue(false)
+ mItems.postValue(HistoryResult.Success(it.orders))
+ }
}
- mItems.value = HistoryResult.Success(items)
}
- @UiThread
- private fun onHistoryError() {
+ private fun onHistoryError(msg: String) = scope.launch(Dispatchers.Main) {
mIsLoading.value = false
- mItems.value = HistoryResult.Error
+ mItems.value = HistoryResult.Error(msg)
}
}
diff --git
a/merchant-terminal/src/main/java/net/taler/merchantpos/history/MerchantHistoryFragment.kt
b/merchant-terminal/src/main/java/net/taler/merchantpos/history/MerchantHistoryFragment.kt
index 6da3dd2..25805dc 100644
---
a/merchant-terminal/src/main/java/net/taler/merchantpos/history/MerchantHistoryFragment.kt
+++
b/merchant-terminal/src/main/java/net/taler/merchantpos/history/MerchantHistoryFragment.kt
@@ -20,6 +20,8 @@ import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
import android.view.ViewGroup
import android.widget.ImageButton
import android.widget.TextView
@@ -31,21 +33,22 @@ import
androidx.recyclerview.widget.DividerItemDecoration.VERTICAL
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView.Adapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
-import com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_SHORT
+import com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_LONG
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.fragment_merchant_history.*
import net.taler.common.exhaustive
import net.taler.common.navigate
import net.taler.common.toRelativeTime
+import net.taler.merchantlib.OrderHistoryEntry
import net.taler.merchantpos.MainViewModel
import net.taler.merchantpos.R
import net.taler.merchantpos.history.HistoryItemAdapter.HistoryItemViewHolder
import
net.taler.merchantpos.history.MerchantHistoryFragmentDirections.Companion.actionGlobalMerchantSettings
import
net.taler.merchantpos.history.MerchantHistoryFragmentDirections.Companion.actionNavHistoryToRefundFragment
-import java.util.*
+import java.util.ArrayList
private interface RefundClickListener {
- fun onRefundClicked(item: HistoryItem)
+ fun onRefundClicked(item: OrderHistoryEntry)
}
/**
@@ -87,7 +90,7 @@ class MerchantHistoryFragment : Fragment(),
RefundClickListener {
})
historyManager.items.observe(viewLifecycleOwner, Observer { result ->
when (result) {
- is HistoryResult.Error -> onError()
+ is HistoryResult.Error -> onError(result.msg)
is HistoryResult.Success ->
historyListAdapter.setData(result.items)
}.exhaustive
})
@@ -95,18 +98,18 @@ class MerchantHistoryFragment : Fragment(),
RefundClickListener {
override fun onStart() {
super.onStart()
- if (model.configManager.merchantConfig?.instance == null) {
+ if (model.configManager.merchantConfig?.baseUrl == null) {
navigate(actionGlobalMerchantSettings())
} else {
historyManager.fetchHistory()
}
}
- private fun onError() {
- Snackbar.make(requireView(), R.string.error_network,
LENGTH_SHORT).show()
+ private fun onError(msg: String) {
+ Snackbar.make(requireView(), msg, LENGTH_LONG).show()
}
- override fun onRefundClicked(item: HistoryItem) {
+ override fun onRefundClicked(item: OrderHistoryEntry) {
refundManager.startRefund(item)
navigate(actionNavHistoryToRefundFragment())
}
@@ -116,7 +119,7 @@ class MerchantHistoryFragment : Fragment(),
RefundClickListener {
private class HistoryItemAdapter(private val listener: RefundClickListener) :
Adapter<HistoryItemViewHolder>() {
- private val items = ArrayList<HistoryItem>()
+ private val items = ArrayList<OrderHistoryEntry>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
HistoryItemViewHolder {
val v =
@@ -130,7 +133,7 @@ private class HistoryItemAdapter(private val listener:
RefundClickListener) :
holder.bind(items[position])
}
- fun setData(items: List<HistoryItem>) {
+ fun setData(items: List<OrderHistoryEntry>) {
this.items.clear()
this.items.addAll(items)
this.notifyDataSetChanged()
@@ -144,13 +147,18 @@ private class HistoryItemAdapter(private val listener:
RefundClickListener) :
private val orderIdView: TextView = v.findViewById(R.id.orderIdView)
private val refundButton: ImageButton =
v.findViewById(R.id.refundButton)
- fun bind(item: HistoryItem) {
+ fun bind(item: OrderHistoryEntry) {
orderSummaryView.text = item.summary
val amount = item.amount
orderAmountView.text = amount.toString()
orderIdView.text = v.context.getString(R.string.history_ref_no,
item.orderId)
- orderTimeView.text = item.time.toRelativeTime(v.context)
- refundButton.setOnClickListener { listener.onRefundClicked(item) }
+ orderTimeView.text = item.timestamp.ms.toRelativeTime(v.context)
+ if (item.refundable) {
+ refundButton.visibility = VISIBLE
+ refundButton.setOnClickListener {
listener.onRefundClicked(item) }
+ } else {
+ refundButton.visibility = GONE
+ }
}
}
diff --git
a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundFragment.kt
b/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundFragment.kt
index eb3d46d..17d78f6 100644
---
a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundFragment.kt
+++
b/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundFragment.kt
@@ -33,6 +33,7 @@ import net.taler.common.AmountParserException
import net.taler.common.fadeIn
import net.taler.common.fadeOut
import net.taler.common.navigate
+import net.taler.merchantlib.OrderHistoryEntry
import net.taler.merchantpos.MainViewModel
import net.taler.merchantpos.R
import
net.taler.merchantpos.history.RefundFragmentDirections.Companion.actionRefundFragmentToRefundUriFragment
@@ -65,7 +66,7 @@ class RefundFragment : Fragment() {
})
}
- private fun onRefundButtonClicked(item: HistoryItem) {
+ private fun onRefundButtonClicked(item: OrderHistoryEntry) {
val inputAmount = try {
Amount.fromString(item.amount.currency,
amountInputView.text.toString())
} catch (e: AmountParserException) {
diff --git
a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundManager.kt
b/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundManager.kt
index da642d6..7f9b4c5 100644
---
a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundManager.kt
+++
b/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundManager.kt
@@ -25,6 +25,7 @@ import com.android.volley.RequestQueue
import com.android.volley.Response.Listener
import com.android.volley.VolleyError
import net.taler.common.Amount
+import net.taler.merchantlib.OrderHistoryEntry
import net.taler.merchantpos.LogErrorListener
import net.taler.merchantpos.config.ConfigManager
import net.taler.merchantpos.config.MerchantRequest
@@ -36,7 +37,7 @@ sealed class RefundResult {
object AlreadyRefunded : RefundResult()
class Success(
val refundUri: String,
- val item: HistoryItem,
+ val item: OrderHistoryEntry,
val amount: Amount,
val reason: String
) : RefundResult()
@@ -51,14 +52,14 @@ class RefundManager(
val TAG = RefundManager::class.java.simpleName
}
- var toBeRefunded: HistoryItem? = null
+ var toBeRefunded: OrderHistoryEntry? = null
private set
private val mRefundResult = MutableLiveData<RefundResult>()
internal val refundResult: LiveData<RefundResult> = mRefundResult
@UiThread
- internal fun startRefund(item: HistoryItem) {
+ internal fun startRefund(item: OrderHistoryEntry) {
toBeRefunded = item
mRefundResult.value = null
}
@@ -70,7 +71,7 @@ class RefundManager(
}
@UiThread
- internal fun refund(item: HistoryItem, amount: Amount, reason: String) {
+ internal fun refund(item: OrderHistoryEntry, amount: Amount, reason:
String) {
val merchantConfig = configManager.merchantConfig!!
val refundRequest = mapOf(
"order_id" to item.orderId,
@@ -89,7 +90,7 @@ class RefundManager(
@UiThread
private fun onRefundResponse(
json: JSONObject,
- item: HistoryItem,
+ item: OrderHistoryEntry,
amount: Amount,
reason: String
) {
diff --git a/settings.gradle b/settings.gradle
index 14d898d..6175852 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,4 +1,7 @@
include ':cashier', ':merchant-terminal', ':wallet'
include ':taler-kotlin-common'
+include ':taler-kotlin-android'
include ':merchant-lib'
include ':anastasis-ui'
+
+enableFeaturePreview('GRADLE_METADATA')
diff --git a/cashier/.gitignore b/taler-kotlin-android/.gitignore
similarity index 100%
copy from cashier/.gitignore
copy to taler-kotlin-android/.gitignore
diff --git a/taler-kotlin-android/.gitlab-ci.yml
b/taler-kotlin-android/.gitlab-ci.yml
new file mode 100644
index 0000000..bb5af21
--- /dev/null
+++ b/taler-kotlin-android/.gitlab-ci.yml
@@ -0,0 +1,12 @@
+taler_kotlin_android_test:
+ stage: test
+ only:
+ changes:
+ - taler-kotlin-android/**/*
+ - taler-kotlin-common/**/*
+ - build.gradle
+ script: ./gradlew :taler-kotlin-android:check
+ artifacts:
+ paths:
+ - taler-kotlin-android/build/reports/lint-results.html
+ expire_in: 1 week
diff --git a/taler-kotlin-common/build.gradle
b/taler-kotlin-android/build.gradle
similarity index 95%
copy from taler-kotlin-common/build.gradle
copy to taler-kotlin-android/build.gradle
index dd083b7..d6d6003 100644
--- a/taler-kotlin-common/build.gradle
+++ b/taler-kotlin-android/build.gradle
@@ -43,9 +43,15 @@ android {
}
}
+ packagingOptions {
+ exclude("META-INF/*.kotlin_module")
+ }
+
}
dependencies {
+ api project(":taler-kotlin-common")
+
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.3.0'
diff --git a/taler-kotlin-common/consumer-rules.pro
b/taler-kotlin-android/consumer-rules.pro
similarity index 100%
rename from taler-kotlin-common/consumer-rules.pro
rename to taler-kotlin-android/consumer-rules.pro
diff --git a/taler-kotlin-common/proguard-rules.pro
b/taler-kotlin-android/proguard-rules.pro
similarity index 100%
rename from taler-kotlin-common/proguard-rules.pro
rename to taler-kotlin-android/proguard-rules.pro
diff --git a/taler-kotlin-common/src/main/AndroidManifest.xml
b/taler-kotlin-android/src/main/AndroidManifest.xml
similarity index 100%
rename from taler-kotlin-common/src/main/AndroidManifest.xml
rename to taler-kotlin-android/src/main/AndroidManifest.xml
diff --git a/taler-kotlin-android/src/main/java/net/taler/common/AmountMixin.kt
b/taler-kotlin-android/src/main/java/net/taler/common/AmountMixin.kt
new file mode 100644
index 0000000..f9b1330
--- /dev/null
+++ b/taler-kotlin-android/src/main/java/net/taler/common/AmountMixin.kt
@@ -0,0 +1,51 @@
+/*
+ * 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 com.fasterxml.jackson.core.JsonGenerator
+import com.fasterxml.jackson.core.JsonParser
+import com.fasterxml.jackson.databind.DeserializationContext
+import com.fasterxml.jackson.databind.JsonMappingException
+import com.fasterxml.jackson.databind.SerializerProvider
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize
+import com.fasterxml.jackson.databind.annotation.JsonSerialize
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer
+import com.fasterxml.jackson.databind.ser.std.StdSerializer
+
+/**
+ * Used to support Jackson serialization along with KotlinX.
+ */
+@JsonSerialize(using = AmountSerializer::class)
+@JsonDeserialize(using = AmountDeserializer::class)
+abstract class AmountMixin
+
+class AmountSerializer : StdSerializer<Amount>(Amount::class.java) {
+ override fun serialize(value: Amount, gen: JsonGenerator, provider:
SerializerProvider) {
+ gen.writeString(value.toJSONString())
+ }
+}
+
+class AmountDeserializer : StdDeserializer<Amount>(Amount::class.java) {
+ override fun deserialize(p: JsonParser, ctxt: DeserializationContext):
Amount {
+ val node = p.codec.readValue(p, String::class.java)
+ try {
+ return Amount.fromJSONString(node)
+ } catch (e: AmountParserException) {
+ throw JsonMappingException(p, "Error parsing Amount", e)
+ }
+ }
+}
diff --git a/taler-kotlin-common/src/main/java/net/taler/common/AndroidUtils.kt
b/taler-kotlin-android/src/main/java/net/taler/common/AndroidUtils.kt
similarity index 100%
rename from taler-kotlin-common/src/main/java/net/taler/common/AndroidUtils.kt
rename to taler-kotlin-android/src/main/java/net/taler/common/AndroidUtils.kt
diff --git
a/taler-kotlin-common/src/main/java/net/taler/common/ByteArrayUtils.kt
b/taler-kotlin-android/src/main/java/net/taler/common/ByteArrayUtils.kt
similarity index 100%
rename from taler-kotlin-common/src/main/java/net/taler/common/ByteArrayUtils.kt
rename to taler-kotlin-android/src/main/java/net/taler/common/ByteArrayUtils.kt
diff --git
a/taler-kotlin-common/src/main/java/net/taler/common/CombinedLiveData.kt
b/taler-kotlin-android/src/main/java/net/taler/common/CombinedLiveData.kt
similarity index 100%
rename from
taler-kotlin-common/src/main/java/net/taler/common/CombinedLiveData.kt
rename to
taler-kotlin-android/src/main/java/net/taler/common/CombinedLiveData.kt
diff --git
a/taler-kotlin-common/src/main/java/net/taler/common/ContractTerms.kt
b/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
similarity index 95%
rename from taler-kotlin-common/src/main/java/net/taler/common/ContractTerms.kt
rename to taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
index c07127a..0d5fe5b 100644
--- a/taler-kotlin-common/src/main/java/net/taler/common/ContractTerms.kt
+++ b/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
@@ -18,7 +18,6 @@ package net.taler.common
import androidx.annotation.RequiresApi
import com.fasterxml.jackson.annotation.JsonIgnore
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY
import com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL
@@ -28,13 +27,14 @@ import kotlinx.serialization.Serializable
import net.taler.common.TalerUtils.getLocalizedString
@Serializable
-@JsonIgnoreProperties(ignoreUnknown = true)
data class ContractTerms(
val summary: String,
@SerialName("summary_i18n")
+ @get:JsonProperty("summary_i18n")
val summaryI18n: Map<String, String>? = null,
val amount: Amount,
@SerialName("fulfillment_url")
+ @get:JsonProperty("fulfillment_url")
val fulfillmentUrl: String,
val products: List<ContractProduct>
)
@@ -82,8 +82,10 @@ data class ContractMerchant(
val name: String
)
+@Serializable
@JsonInclude(NON_EMPTY)
class Timestamp(
+ @SerialName("t_ms")
@JsonProperty("t_ms")
val ms: Long
)
diff --git a/taler-kotlin-common/src/main/java/net/taler/common/Event.kt
b/taler-kotlin-android/src/main/java/net/taler/common/Event.kt
similarity index 100%
rename from taler-kotlin-common/src/main/java/net/taler/common/Event.kt
rename to taler-kotlin-android/src/main/java/net/taler/common/Event.kt
diff --git a/taler-kotlin-common/src/main/java/net/taler/common/NfcManager.kt
b/taler-kotlin-android/src/main/java/net/taler/common/NfcManager.kt
similarity index 100%
rename from taler-kotlin-common/src/main/java/net/taler/common/NfcManager.kt
rename to taler-kotlin-android/src/main/java/net/taler/common/NfcManager.kt
diff --git
a/taler-kotlin-common/src/main/java/net/taler/common/QrCodeManager.kt
b/taler-kotlin-android/src/main/java/net/taler/common/QrCodeManager.kt
similarity index 100%
rename from taler-kotlin-common/src/main/java/net/taler/common/QrCodeManager.kt
rename to taler-kotlin-android/src/main/java/net/taler/common/QrCodeManager.kt
diff --git a/taler-kotlin-common/src/main/java/net/taler/common/SignedAmount.kt
b/taler-kotlin-android/src/main/java/net/taler/common/SignedAmount.kt
similarity index 100%
rename from taler-kotlin-common/src/main/java/net/taler/common/SignedAmount.kt
rename to taler-kotlin-android/src/main/java/net/taler/common/SignedAmount.kt
diff --git a/taler-kotlin-common/src/main/java/net/taler/common/TalerUtils.kt
b/taler-kotlin-android/src/main/java/net/taler/common/TalerUtils.kt
similarity index 97%
rename from taler-kotlin-common/src/main/java/net/taler/common/TalerUtils.kt
rename to taler-kotlin-android/src/main/java/net/taler/common/TalerUtils.kt
index 444caa4..bb2e78a 100644
--- a/taler-kotlin-common/src/main/java/net/taler/common/TalerUtils.kt
+++ b/taler-kotlin-android/src/main/java/net/taler/common/TalerUtils.kt
@@ -18,8 +18,7 @@ package net.taler.common
import androidx.annotation.RequiresApi
import androidx.core.os.LocaleListCompat
-import java.util.*
-import kotlin.collections.ArrayList
+import java.util.Locale
object TalerUtils {
diff --git
a/taler-kotlin-common/src/main/res/drawable/selectable_background.xml
b/taler-kotlin-android/src/main/res/drawable/selectable_background.xml
similarity index 100%
rename from taler-kotlin-common/src/main/res/drawable/selectable_background.xml
rename to taler-kotlin-android/src/main/res/drawable/selectable_background.xml
diff --git a/taler-kotlin-common/src/main/res/values-night/colors.xml
b/taler-kotlin-android/src/main/res/values-night/colors.xml
similarity index 100%
rename from taler-kotlin-common/src/main/res/values-night/colors.xml
rename to taler-kotlin-android/src/main/res/values-night/colors.xml
diff --git a/taler-kotlin-common/src/main/res/values/colors.xml
b/taler-kotlin-android/src/main/res/values/colors.xml
similarity index 100%
rename from taler-kotlin-common/src/main/res/values/colors.xml
rename to taler-kotlin-android/src/main/res/values/colors.xml
diff --git a/taler-kotlin-common/src/main/res/values/strings.xml
b/taler-kotlin-android/src/main/res/values/strings.xml
similarity index 100%
rename from taler-kotlin-common/src/main/res/values/strings.xml
rename to taler-kotlin-android/src/main/res/values/strings.xml
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
new file mode 100644
index 0000000..79a7598
--- /dev/null
+++ b/taler-kotlin-android/src/test/java/net/taler/common/ContractTermsTest.kt
@@ -0,0 +1,74 @@
+/*
+ * 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 com.fasterxml.jackson.databind.DeserializationFeature
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.kotlin.KotlinModule
+import com.fasterxml.jackson.module.kotlin.readValue
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class ContractTermsTest {
+
+ private val mapper = ObjectMapper()
+ .registerModule(KotlinModule())
+ .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
+ .addMixIn(Amount::class.java, AmountMixin::class.java)
+
+ @Test
+ fun test() {
+ val json = """
+ {
+ "amount":"TESTKUDOS:0.5",
+ "extra":{
+ "article_name":"1._The_Free_Software_Definition"
+ },
+
"fulfillment_url":"https://shop.test.taler.net/essay/1._The_Free_Software_Definition",
+ "summary":"Essay: 1. The Free Software Definition",
+ "refund_deadline":{"t_ms":1596128414000},
+ "wire_transfer_deadline":{"t_ms":1596128564000},
+ "products":[],
+
"h_wire":"KV40K023N8EC1F5100TYNS23C4XN68Y1Z3PTJSWFGTMCNYD54KT4S791V2VQ91SZANN86VDAA369M4VEZ0KR6DN71EVRRZA71K681M0",
+ "wire_method":"x-taler-bank",
+ "order_id":"2020.212-01M9VKEAPF76C",
+ "timestamp":{"t_ms":1596128114000},
+ "pay_deadline":{"t_ms":"never"},
+ "max_wire_fee":"TESTKUDOS:1",
+ "max_fee":"TESTKUDOS:1",
+ "wire_fee_amortization":3,
+
"merchant_base_url":"https://backend.test.taler.net/instances/blog/",
+ "merchant":{"name":"Blog","instance":"blog"},
+ "exchanges":[
+ {
+ "url":"https://exchange.test.taler.net/",
+
"master_pub":"DY95EXAHQ2BKM2WK9YHZHYG1R7PPMMJPY14FNGP662DAKE35AKQG"
+ },
+ {
+ "url":"https://exchange.test.taler.net/",
+
"master_pub":"DY95EXAHQ2BKM2WK9YHZHYG1R7PPMMJPY14FNGP662DAKE35AKQG"}
+ ],
+ "auditors":[],
+
"merchant_pub":"8DR9NKSZY1CXFRE47NEYXM0K85C4ZGAYH7Y7VZ22GPNF0BRFNYNG",
+ "nonce":"FK8ZKJRV6VX6YFAG4CDSC6W0DWD084Q09DP81ANF30GRFQYM2KPG"
+ }
+ """.trimIndent()
+ val contractTerms: ContractTerms = mapper.readValue(json)
+ assertEquals("Essay: 1. The Free Software Definition",
contractTerms.summary)
+ }
+
+}
diff --git a/taler-kotlin-common/.gitlab-ci.yml
b/taler-kotlin-common/.gitlab-ci.yml
index 49d3e98..c241e31 100644
--- a/taler-kotlin-common/.gitlab-ci.yml
+++ b/taler-kotlin-common/.gitlab-ci.yml
@@ -4,8 +4,4 @@ taler_kotlin_common_test:
changes:
- taler-kotlin-common/**/*
- build.gradle
- script: ./gradlew :taler-kotlin-common:check
- artifacts:
- paths:
- - taler-kotlin-common/build/reports/lint-results.html
- expire_in: 1 week
+ script: ./gradlew :taler-kotlin-common:jvmTest
diff --git a/taler-kotlin-common/build.gradle b/taler-kotlin-common/build.gradle
index dd083b7..129881d 100644
--- a/taler-kotlin-common/build.gradle
+++ b/taler-kotlin-common/build.gradle
@@ -1,72 +1,82 @@
-/*
- * 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/>
- */
-
plugins {
- id 'com.android.library'
- id 'kotlin-android'
- id 'kotlin-android-extensions'
+ id 'org.jetbrains.kotlin.multiplatform'
id 'kotlinx-serialization'
}
-android {
- compileSdkVersion 29
- //noinspection GradleDependency
- buildToolsVersion "$build_tools_version"
+group 'net.taler'
+version '0.0.1'
- defaultConfig {
- minSdkVersion 24
- targetSdkVersion 29
- versionCode 1
- versionName "0.1"
+apply plugin: 'maven-publish'
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles 'consumer-rules.pro'
+kotlin {
+ jvm()
+ // This is for iPhone simulator
+ // Switch here to iosArm64 (or iosArm32) to build library for iPhone device
+ iosX64("ios") {
+ binaries {
+ framework()
+ }
}
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles
getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ linuxX64("linux")
+ js {
+ browser {
+ }
+ nodejs {
+ }
+ }
+ sourceSets {
+ def serialization_version = "0.20.0"
+ commonMain {
+ dependencies {
+ implementation kotlin('stdlib-common')
+ implementation
"org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version"
+ }
+ }
+ commonTest {
+ dependencies {
+ implementation kotlin('test-common')
+ implementation kotlin('test-annotations-common')
+ }
+ }
+ jvmMain {
+ dependencies {
+ implementation kotlin('stdlib-jdk8')
+ implementation
"org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version"
+ }
+ }
+ jvmTest {
+ dependencies {
+ implementation kotlin('test')
+ implementation kotlin('test-junit')
+ }
+ }
+ jsMain {
+ dependencies {
+ implementation kotlin('stdlib-js')
+ implementation
"org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version"
+ }
+ }
+ jsTest {
+ dependencies {
+ implementation kotlin('test-js')
+ }
+ }
+ nativeMain {
+ dependsOn commonMain
+ dependencies {
+ implementation
"org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:$serialization_version"
+ }
+ }
+ nativeTest {
+ dependsOn commonTest
+ }
+ configure([targets.linux, targets.ios]) {
+ compilations.main.source(sourceSets.nativeMain)
+ compilations.test.source(sourceSets.nativeTest)
}
}
-
}
-dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
- implementation 'androidx.appcompat:appcompat:1.1.0'
- implementation 'androidx.core:core-ktx:1.3.0'
-
- // Navigation
- implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
- implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
-
- // ViewModel and LiveData
- def lifecycle_version = "2.2.0"
- implementation
"androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
-
- // QR codes
- implementation 'com.google.zxing:core:3.4.0' // needs minSdkVersion 24+
-
- // JSON parsing and serialization
- api "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0"
- implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.10.2"
-
- lintChecks 'com.github.thirdegg:lint-rules:0.0.4-alpha'
-
- testImplementation 'junit:junit:4.13'
- testImplementation 'org.json:json:20190722'
+configurations {
+ compileClasspath
}
diff --git a/taler-kotlin-common/src/main/java/net/taler/common/Amount.kt
b/taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Amount.kt
similarity index 70%
rename from taler-kotlin-common/src/main/java/net/taler/common/Amount.kt
rename to taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Amount.kt
index 992f93b..84d10c5 100644
--- a/taler-kotlin-common/src/main/java/net/taler/common/Amount.kt
+++ b/taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Amount.kt
@@ -16,23 +16,12 @@
package net.taler.common
-import android.annotation.SuppressLint
-import com.fasterxml.jackson.core.JsonGenerator
-import com.fasterxml.jackson.core.JsonParser
-import com.fasterxml.jackson.databind.DeserializationContext
-import com.fasterxml.jackson.databind.JsonMappingException
-import com.fasterxml.jackson.databind.SerializerProvider
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize
-import com.fasterxml.jackson.databind.annotation.JsonSerialize
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer
-import com.fasterxml.jackson.databind.ser.std.StdSerializer
import kotlinx.serialization.Decoder
import kotlinx.serialization.Encoder
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.Serializer
-import org.json.JSONObject
-import java.lang.Math.floorDiv
+import kotlin.math.floor
import kotlin.math.pow
import kotlin.math.roundToInt
@@ -40,8 +29,6 @@ class AmountParserException(msg: String? = null, cause:
Throwable? = null) : Exc
class AmountOverflowException(msg: String? = null, cause: Throwable? = null) :
Exception(msg, cause)
@Serializable(with = KotlinXAmountSerializer::class)
-@JsonSerialize(using = AmountSerializer::class)
-@JsonDeserialize(using = AmountDeserializer::class)
data class Amount(
/**
* name of the currency using either a three-character ISO 4217 currency
code,
@@ -70,29 +57,21 @@ data class Amount(
private const val FRACTIONAL_BASE: Int = 100000000 // 1e8
- @Suppress("unused")
- private val REGEX =
Regex("""^[-_*A-Za-z0-9]{1,12}:([0-9]+)\.?([0-9]+)?$""")
private val REGEX_CURRENCY = Regex("""^[-_*A-Za-z0-9]{1,12}$""")
- private val MAX_VALUE = 2.0.pow(52)
+ val MAX_VALUE = 2.0.pow(52).toLong()
private const val MAX_FRACTION_LENGTH = 8
- private const val MAX_FRACTION = 99_999_999
+ const val MAX_FRACTION = 99_999_999
- @Throws(AmountParserException::class)
- @SuppressLint("CheckedExceptions")
fun zero(currency: String): Amount {
return Amount(checkCurrency(currency), 0, 0)
}
- @Throws(AmountParserException::class)
- @SuppressLint("CheckedExceptions")
fun fromJSONString(str: String): Amount {
val split = str.split(":")
if (split.size != 2) throw AmountParserException("Invalid Amount
Format")
return fromString(split[0], split[1])
}
- @Throws(AmountParserException::class)
- @SuppressLint("CheckedExceptions")
fun fromString(currency: String, str: String): Amount {
// value
val valueSplit = str.split(".")
@@ -110,31 +89,23 @@ data class Amount(
return Amount(checkCurrency(currency), value, fraction)
}
- @Throws(AmountParserException::class)
- @SuppressLint("CheckedExceptions")
- fun fromJsonObject(json: JSONObject): Amount {
- val currency = checkCurrency(json.optString("currency"))
- val value = checkValue(json.optString("value").toLongOrNull())
- val fraction =
checkFraction(json.optString("fraction").toIntOrNull())
- return Amount(currency, value, fraction)
- }
+ fun min(currency: String): Amount = Amount(currency, 0, 1)
+ fun max(currency: String): Amount = Amount(currency, MAX_VALUE,
MAX_FRACTION)
+
- @Throws(AmountParserException::class)
- private fun checkCurrency(currency: String): String {
+ internal fun checkCurrency(currency: String): String {
if (!REGEX_CURRENCY.matches(currency))
throw AmountParserException("Invalid currency: $currency")
return currency
}
- @Throws(AmountParserException::class)
- private fun checkValue(value: Long?): Long {
+ internal fun checkValue(value: Long?): Long {
if (value == null || value > MAX_VALUE)
throw AmountParserException("Value $value greater than
$MAX_VALUE")
return value
}
- @Throws(AmountParserException::class)
- private fun checkFraction(fraction: Int?): Int {
+ internal fun checkFraction(fraction: Int?): Int {
if (fraction == null || fraction > MAX_FRACTION)
throw AmountParserException("Fraction $fraction greater than
$MAX_FRACTION")
return fraction
@@ -153,25 +124,23 @@ data class Amount(
"$value.$fractionStr"
}
- @Throws(AmountOverflowException::class)
operator fun plus(other: Amount): Amount {
check(currency == other.currency) { "Can only subtract from same
currency" }
- val resultValue = value + other.value + floorDiv(fraction +
other.fraction, FRACTIONAL_BASE)
+ 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)
}
- @Throws(AmountOverflowException::class)
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
}
- @Throws(AmountOverflowException::class)
operator fun minus(other: Amount): Amount {
check(currency == other.currency) { "Can only subtract from same
currency" }
var resultValue = value
@@ -227,20 +196,3 @@ object KotlinXAmountSerializer: KSerializer<Amount> {
return Amount.fromJSONString(decoder.decodeString())
}
}
-
-class AmountSerializer : StdSerializer<Amount>(Amount::class.java) {
- override fun serialize(value: Amount, gen: JsonGenerator, provider:
SerializerProvider) {
- gen.writeString(value.toJSONString())
- }
-}
-
-class AmountDeserializer : StdDeserializer<Amount>(Amount::class.java) {
- override fun deserialize(p: JsonParser, ctxt: DeserializationContext):
Amount {
- val node = p.codec.readValue(p, String::class.java)
- try {
- return Amount.fromJSONString(node)
- } catch (e: AmountParserException) {
- throw JsonMappingException(p, "Error parsing Amount", e)
- }
- }
-}
diff --git a/taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Time.kt
b/taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Time.kt
new file mode 100644
index 0000000..962e004
--- /dev/null
+++ b/taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Time.kt
@@ -0,0 +1,81 @@
+/*
+ * 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 net.taler.common.Duration.Companion.FOREVER
+import kotlin.math.max
+
+expect fun nowMillis(): Long
+
+@Serializable
+data class Timestamp(
+ @SerialName("t_ms")
+ val ms: Long
+) : Comparable<Timestamp> {
+
+ companion object {
+ const val NEVER: Long = -1 // TODO or UINT64_MAX?
+ fun now(): Timestamp = Timestamp(nowMillis())
+ }
+
+ /**
+ * 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(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 == 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(
+ /**
+ * Duration in milliseconds.
+ */
+ @SerialName("d_ms")
+ val ms: Long
+) {
+ companion object {
+ const val FOREVER: Long = -1 // TODO or UINT64_MAX?
+ }
+}
diff --git a/taler-kotlin-common/src/main/java/net/taler/common/Version.kt
b/taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Version.kt
similarity index 100%
rename from taler-kotlin-common/src/main/java/net/taler/common/Version.kt
rename to taler-kotlin-common/src/commonMain/kotlin/net/taler/common/Version.kt
diff --git a/taler-kotlin-common/src/test/java/net/taler/common/AmountTest.kt
b/taler-kotlin-common/src/commonTest/kotlin/net/taler/common/AmountTest.kt
similarity index 65%
rename from taler-kotlin-common/src/test/java/net/taler/common/AmountTest.kt
rename to
taler-kotlin-common/src/commonTest/kotlin/net/taler/common/AmountTest.kt
index 97d9667..e184307 100644
--- a/taler-kotlin-common/src/test/java/net/taler/common/AmountTest.kt
+++ b/taler-kotlin-common/src/commonTest/kotlin/net/taler/common/AmountTest.kt
@@ -16,20 +16,26 @@
package net.taler.common
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.module.kotlin.KotlinModule
-import com.fasterxml.jackson.module.kotlin.readValue
-import org.json.JSONObject
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Assert.fail
-import org.junit.Test
+import kotlin.random.Random
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+import kotlin.test.fail
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 `test fromJSONString() works`() {
+ fun testFromJSONString() {
var str = "TESTKUDOS:23.42"
var amount = Amount.fromJSONString(str)
assertEquals(str, amount.toJSONString())
@@ -56,7 +62,7 @@ class AmountTest {
}
@Test
- fun `test fromJSONString() accepts max values, rejects above`() {
+ fun testFromJSONStringAcceptsMaxValuesRejectsAbove() {
val maxValue = 4503599627370496
val str = "TESTKUDOS123:$maxValue.99999999"
val amount = Amount.fromJSONString(str)
@@ -82,35 +88,7 @@ class AmountTest {
}
@Test
- fun `test JSON deserialization()`() {
- val mapper = ObjectMapper().registerModule(KotlinModule())
- var str = "TESTKUDOS:23.42"
- var amount: Amount = mapper.readValue("\"$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 = mapper.readValue("\"$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 = mapper.readValue("\"$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 `test fromJSONString() rejections`() {
+ fun testFromJSONStringRejections() {
assertThrows<AmountParserException> {
Amount.fromJSONString("TESTKUDOS:0,5")
}
@@ -132,71 +110,7 @@ class AmountTest {
}
@Test
- fun `test fromJsonObject() works`() {
- val map = mapOf(
- "currency" to "TESTKUDOS",
- "value" to "23",
- "fraction" to "42000000"
- )
-
- val amount = Amount.fromJsonObject(JSONObject(map))
- assertEquals("TESTKUDOS:23.42", amount.toJSONString())
- assertEquals("TESTKUDOS", amount.currency)
- assertEquals(23, amount.value)
- assertEquals(42000000, amount.fraction)
- assertEquals("23.42 TESTKUDOS", amount.toString())
- }
-
- @Test
- fun `test fromJsonObject() accepts max values, rejects above`() {
- val maxValue = 4503599627370496
- val maxFraction = 99999999
- var map = mapOf(
- "currency" to "TESTKUDOS123",
- "value" to "$maxValue",
- "fraction" to "$maxFraction"
- )
-
- val amount = Amount.fromJsonObject(JSONObject(map))
- assertEquals("TESTKUDOS123:$maxValue.$maxFraction",
amount.toJSONString())
- assertEquals("TESTKUDOS123", amount.currency)
- assertEquals(maxValue, amount.value)
- assertEquals(maxFraction, amount.fraction)
- assertEquals("$maxValue.$maxFraction TESTKUDOS123", amount.toString())
-
- // longer currency not accepted
- assertThrows<AmountParserException>("longer currency was accepted") {
- map = mapOf(
- "currency" to "TESTKUDOS1234",
- "value" to "$maxValue",
- "fraction" to "$maxFraction"
- )
- Amount.fromJsonObject(JSONObject(map))
- }
-
- // max value + 1 not accepted
- assertThrows<AmountParserException>("max value + 1 was accepted") {
- map = mapOf(
- "currency" to "TESTKUDOS123",
- "value" to "${maxValue + 1}",
- "fraction" to "$maxFraction"
- )
- Amount.fromJsonObject(JSONObject(map))
- }
-
- // max fraction + 1 not accepted
- assertThrows<AmountParserException>("max fraction + 1 was accepted") {
- map = mapOf(
- "currency" to "TESTKUDOS123",
- "value" to "$maxValue",
- "fraction" to "${maxFraction + 1}"
- )
- Amount.fromJsonObject(JSONObject(map))
- }
- }
-
- @Test
- fun `test addition`() {
+ fun testAddition() {
assertEquals(
Amount.fromJSONString("EUR:2"),
Amount.fromJSONString("EUR:1") + Amount.fromJSONString("EUR:1")
@@ -218,7 +132,7 @@ class AmountTest {
}
@Test
- fun `test times`() {
+ fun testTimes() {
assertEquals(
Amount.fromJSONString("EUR:2"),
Amount.fromJSONString("EUR:2") * 1
@@ -231,6 +145,12 @@ class AmountTest {
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
@@ -241,7 +161,7 @@ class AmountTest {
}
@Test
- fun `test subtraction`() {
+ fun testSubtraction() {
assertEquals(
Amount.fromJSONString("EUR:0"),
Amount.fromJSONString("EUR:1") - Amount.fromJSONString("EUR:1")
@@ -263,7 +183,7 @@ class AmountTest {
}
@Test
- fun `test isZero()`() {
+ fun testIsZero() {
assertTrue(Amount.zero("EUR").isZero())
assertTrue(Amount.fromJSONString("EUR:0").isZero())
assertTrue(Amount.fromJSONString("EUR:0.0").isZero())
@@ -276,14 +196,17 @@ class AmountTest {
}
@Test
- fun `test comparision`() {
+ 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"))
- assertTrue(Amount.fromJSONString("EUR:0") ==
Amount.fromJSONString("EUR:0"))
- assertTrue(Amount.fromJSONString("EUR:42") ==
Amount.fromJSONString("EUR:42"))
- assertTrue(Amount.fromJSONString("EUR:42.00000001") ==
Amount.fromJSONString("EUR:42.00000001"))
+ 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"))
diff --git
a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/Payment.kt
b/taler-kotlin-common/src/commonTest/kotlin/net/taler/common/TestUtils.kt
similarity index 67%
copy from
merchant-terminal/src/main/java/net/taler/merchantpos/payment/Payment.kt
copy to taler-kotlin-common/src/commonTest/kotlin/net/taler/common/TestUtils.kt
index 9200ced..e3a6c17 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/Payment.kt
+++ b/taler-kotlin-common/src/commonTest/kotlin/net/taler/common/TestUtils.kt
@@ -14,16 +14,13 @@
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-package net.taler.merchantpos.payment
+package net.taler.common
-import net.taler.merchantpos.order.Order
+import kotlin.random.Random
-data class Payment(
- val order: Order,
- val summary: String,
- val currency: String,
- val orderId: String? = null,
- val talerPayUri: String? = null,
- val paid: Boolean = false,
- val error: String? = null
-)
+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("")
diff --git a/taler-kotlin-common/src/test/java/net/taler/common/VersionTest.kt
b/taler-kotlin-common/src/commonTest/kotlin/net/taler/common/VersionTest.kt
similarity index 95%
rename from taler-kotlin-common/src/test/java/net/taler/common/VersionTest.kt
rename to
taler-kotlin-common/src/commonTest/kotlin/net/taler/common/VersionTest.kt
index 70f30eb..f4f17ea 100644
--- a/taler-kotlin-common/src/test/java/net/taler/common/VersionTest.kt
+++ b/taler-kotlin-common/src/commonTest/kotlin/net/taler/common/VersionTest.kt
@@ -16,9 +16,9 @@
package net.taler.common
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNull
-import org.junit.Test
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
class VersionTest {
diff --git
a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/Payment.kt
b/taler-kotlin-common/src/jsMain/kotlin/net/taler/common/Time.kt
similarity index 70%
copy from
merchant-terminal/src/main/java/net/taler/merchantpos/payment/Payment.kt
copy to taler-kotlin-common/src/jsMain/kotlin/net/taler/common/Time.kt
index 9200ced..b114022 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/Payment.kt
+++ b/taler-kotlin-common/src/jsMain/kotlin/net/taler/common/Time.kt
@@ -14,16 +14,10 @@
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-package net.taler.merchantpos.payment
+package net.taler.common
-import net.taler.merchantpos.order.Order
+import kotlin.js.Date
-data class Payment(
- val order: Order,
- val summary: String,
- val currency: String,
- val orderId: String? = null,
- val talerPayUri: String? = null,
- val paid: Boolean = false,
- val error: String? = null
-)
+actual fun nowMillis(): Long {
+ return Date().getMilliseconds().toLong()
+}
diff --git
a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/Payment.kt
b/taler-kotlin-common/src/jvmMain/kotlin/net/taler/common/Time.kt
similarity index 70%
copy from
merchant-terminal/src/main/java/net/taler/merchantpos/payment/Payment.kt
copy to taler-kotlin-common/src/jvmMain/kotlin/net/taler/common/Time.kt
index 9200ced..6cd9040 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/Payment.kt
+++ b/taler-kotlin-common/src/jvmMain/kotlin/net/taler/common/Time.kt
@@ -14,16 +14,8 @@
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-package net.taler.merchantpos.payment
+package net.taler.common
-import net.taler.merchantpos.order.Order
-
-data class Payment(
- val order: Order,
- val summary: String,
- val currency: String,
- val orderId: String? = null,
- val talerPayUri: String? = null,
- val paid: Boolean = false,
- val error: String? = null
-)
+actual fun nowMillis(): Long {
+ return System.currentTimeMillis()
+}
diff --git
a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/Payment.kt
b/taler-kotlin-common/src/nativeMain/kotlin/net/taler/common/Time.kt
similarity index 70%
copy from
merchant-terminal/src/main/java/net/taler/merchantpos/payment/Payment.kt
copy to taler-kotlin-common/src/nativeMain/kotlin/net/taler/common/Time.kt
index 9200ced..8a4091a 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/Payment.kt
+++ b/taler-kotlin-common/src/nativeMain/kotlin/net/taler/common/Time.kt
@@ -14,16 +14,10 @@
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-package net.taler.merchantpos.payment
+package net.taler.common
-import net.taler.merchantpos.order.Order
+import kotlin.system.getTimeMillis
-data class Payment(
- val order: Order,
- val summary: String,
- val currency: String,
- val orderId: String? = null,
- val talerPayUri: String? = null,
- val paid: Boolean = false,
- val error: String? = null
-)
+actual fun nowMillis(): Long {
+ return getTimeMillis()
+}
diff --git a/wallet/.gitlab-ci.yml b/wallet/.gitlab-ci.yml
index 56768f7..c417aa9 100644
--- a/wallet/.gitlab-ci.yml
+++ b/wallet/.gitlab-ci.yml
@@ -4,6 +4,7 @@ wallet_test:
changes:
- wallet/**/*
- taler-kotlin-common/**/*
+ - taler-kotlin-android/**/*
- build.gradle
script: ./gradlew :wallet:check :wallet:assembleRelease
artifacts:
diff --git a/wallet/build.gradle b/wallet/build.gradle
index 5b28c6c..1761018 100644
--- a/wallet/build.gradle
+++ b/wallet/build.gradle
@@ -23,7 +23,7 @@ plugins {
id "de.undercouch.download"
}
-def walletCoreVersion = "v0.7.1-dev.10"
+def walletCoreVersion = "v0.7.1-dev.16"
static def versionCodeEpoch() {
return (new Date().getTime() / 1000).toInteger()
@@ -47,7 +47,7 @@ android {
minSdkVersion 24
targetSdkVersion 29
versionCode 6
- versionName "0.7.1.dev.10"
+ versionName "0.7.1.dev.16"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField "String", "WALLET_CORE_VERSION",
"\"$walletCoreVersion\""
}
@@ -83,6 +83,10 @@ android {
jvmTarget = "1.8"
}
+ packagingOptions {
+ exclude("META-INF/*.kotlin_module")
+ }
+
lintOptions {
abortOnError true
ignoreWarnings false
@@ -93,7 +97,7 @@ android {
}
dependencies {
- implementation project(":taler-kotlin-common")
+ implementation project(":taler-kotlin-android")
implementation project(":anastasis-ui")
implementation 'net.taler:akono:0.1'
diff --git a/wallet/src/main/java/net/taler/wallet/MainActivity.kt
b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
index fdb8cf8..c7c31ca 100644
--- a/wallet/src/main/java/net/taler/wallet/MainActivity.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
@@ -81,7 +81,7 @@ class MainActivity : AppCompatActivity(),
OnNavigationItemSelectedListener,
setSupportActionBar(toolbar)
val appBarConfiguration = AppBarConfiguration(
- setOf(R.id.nav_main, R.id.nav_settings,
R.id.nav_pending_operations, R.id.nav_history),
+ setOf(R.id.nav_main, R.id.nav_settings,
R.id.nav_pending_operations),
drawer_layout
)
toolbar.setupWithNavController(nav, appBarConfiguration)
@@ -122,7 +122,6 @@ class MainActivity : AppCompatActivity(),
OnNavigationItemSelectedListener,
R.id.nav_home -> nav.navigate(R.id.nav_main)
R.id.nav_settings -> nav.navigate(R.id.nav_settings)
R.id.nav_pending_operations ->
nav.navigate(R.id.nav_pending_operations)
- R.id.nav_history -> nav.navigate(R.id.nav_history)
}
drawer_layout.closeDrawer(START)
return true
@@ -160,7 +159,7 @@ class MainActivity : AppCompatActivity(),
OnNavigationItemSelectedListener,
Log.v(TAG, "navigating!")
// there's more than one entry point, so use global action
nav.navigate(R.id.action_global_promptWithdraw)
- model.withdrawManager.getWithdrawalInfo(url)
+ model.withdrawManager.getWithdrawalDetails(url)
}
url.toLowerCase(ROOT).startsWith("taler://refund/") -> {
model.showProgressBar.value = true
diff --git a/wallet/src/main/java/net/taler/wallet/MainFragment.kt
b/wallet/src/main/java/net/taler/wallet/MainFragment.kt
index a735987..d5bd3fc 100644
--- a/wallet/src/main/java/net/taler/wallet/MainFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainFragment.kt
@@ -49,7 +49,7 @@ class MainFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
model.balances.observe(viewLifecycleOwner, Observer {
- onBalancesChanged(it.values.toList())
+ onBalancesChanged(it)
})
model.transactionsEvent.observe(viewLifecycleOwner, EventObserver {
currency ->
// we only need to navigate to a dedicated list, when in
multi-currency mode
diff --git a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
index 46f5021..ffa2dea 100644
--- a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
@@ -24,17 +24,19 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.viewModelScope
+import com.fasterxml.jackson.databind.DeserializationFeature
import
com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
+import com.fasterxml.jackson.module.kotlin.readValue
import net.taler.common.Amount
+import net.taler.common.AmountMixin
import net.taler.common.Event
import net.taler.common.assertUiThread
import net.taler.common.toEvent
import net.taler.wallet.backend.WalletBackendApi
import net.taler.wallet.balances.BalanceItem
import net.taler.wallet.exchanges.ExchangeManager
-import net.taler.wallet.history.DevHistoryManager
import net.taler.wallet.payment.PaymentManager
import net.taler.wallet.pending.PendingOperationsManager
import net.taler.wallet.refund.RefundManager
@@ -55,8 +57,8 @@ private val transactionNotifications = listOf(
class MainViewModel(val app: Application) : AndroidViewModel(app) {
- private val mBalances = MutableLiveData<Map<String, BalanceItem>>()
- val balances: LiveData<Map<String, BalanceItem>> =
mBalances.distinctUntilChanged()
+ private val mBalances = MutableLiveData<List<BalanceItem>>()
+ val balances: LiveData<List<BalanceItem>> =
mBalances.distinctUntilChanged()
val devMode = MutableLiveData(BuildConfig.DEBUG)
val showProgressBar = MutableLiveData<Boolean>()
@@ -85,7 +87,6 @@ class MainViewModel(val app: Application) :
AndroidViewModel(app) {
// refresh pending ops and history with each notification
if (devMode.value == true) {
pendingOperationsManager.getPending()
- historyManager.loadHistory()
}
}
}
@@ -93,13 +94,13 @@ class MainViewModel(val app: Application) :
AndroidViewModel(app) {
private val mapper = ObjectMapper()
.registerModule(KotlinModule())
.configure(FAIL_ON_UNKNOWN_PROPERTIES, false)
+ .addMixIn(Amount::class.java, AmountMixin::class.java)
+ .enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)
- val withdrawManager = WithdrawManager(walletBackendApi)
+ val withdrawManager = WithdrawManager(walletBackendApi, mapper)
val paymentManager = PaymentManager(walletBackendApi, mapper)
val pendingOperationsManager: PendingOperationsManager =
PendingOperationsManager(walletBackendApi)
- val historyManager: DevHistoryManager =
- DevHistoryManager(walletBackendApi, viewModelScope, mapper)
val transactionManager: TransactionManager =
TransactionManager(walletBackendApi, viewModelScope, mapper)
val refundManager = RefundManager(walletBackendApi)
@@ -123,26 +124,13 @@ class MainViewModel(val app: Application) :
AndroidViewModel(app) {
@UiThread
fun loadBalances() {
showProgressBar.value = true
- walletBackendApi.sendRequest("getBalances", null) { isError, result ->
+ walletBackendApi.sendRequest("getBalances") { isError, result ->
if (isError) {
Log.e(TAG, "Error retrieving balances: ${result.toString(2)}")
return@sendRequest
}
- val balanceMap = HashMap<String, BalanceItem>()
- val byCurrency = result.getJSONObject("byCurrency")
- val currencyList = byCurrency.keys().asSequence().toList().sorted()
- for (currency in currencyList) {
- val jsonAmount = byCurrency.getJSONObject(currency)
- .getJSONObject("available")
- val amount = Amount.fromJsonObject(jsonAmount)
- val jsonAmountIncoming = byCurrency.getJSONObject(currency)
- .getJSONObject("pendingIncoming")
- val amountIncoming = Amount.fromJsonObject(jsonAmountIncoming)
- val hasPending = transactionManager.hasPending(currency)
- balanceMap[currency] = BalanceItem(amount, amountIncoming,
hasPending)
- }
- mBalances.postValue(balanceMap)
- showProgressBar.postValue(false)
+ mBalances.value = mapper.readValue(result.getString("balances"))
+ showProgressBar.value = false
}
}
@@ -156,17 +144,17 @@ class MainViewModel(val app: Application) :
AndroidViewModel(app) {
@UiThread
fun dangerouslyReset() {
- walletBackendApi.sendRequest("reset", null)
+ walletBackendApi.sendRequest("reset")
withdrawManager.testWithdrawalInProgress.value = false
- mBalances.value = emptyMap()
+ mBalances.value = emptyList()
}
fun startTunnel() {
- walletBackendApi.sendRequest("startTunnel", null)
+ walletBackendApi.sendRequest("startTunnel")
}
fun stopTunnel() {
- walletBackendApi.sendRequest("stopTunnel", null)
+ walletBackendApi.sendRequest("stopTunnel")
}
fun tunnelResponse(resp: String) {
diff --git a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
b/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
index 3ffcd7b..51b3419 100644
--- a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
+++ b/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
@@ -30,7 +30,7 @@ import android.util.Log
import android.util.SparseArray
import org.json.JSONObject
import java.lang.ref.WeakReference
-import java.util.*
+import java.util.LinkedList
class WalletBackendApi(
private val app: Application,
@@ -115,7 +115,7 @@ class WalletBackendApi(
fun sendRequest(
operation: String,
- args: JSONObject?,
+ args: JSONObject? = null,
onResponse: (isError: Boolean, message: JSONObject) -> Unit = { _, _
-> }
) {
val requestID = nextRequestID++
diff --git
a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendService.kt
b/wallet/src/main/java/net/taler/wallet/backend/WalletBackendService.kt
index c810054..f39a3e7 100644
--- a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendService.kt
+++ b/wallet/src/main/java/net/taler/wallet/backend/WalletBackendService.kt
@@ -30,7 +30,7 @@ import net.taler.wallet.BuildConfig.WALLET_CORE_VERSION
import net.taler.wallet.HostCardEmulatorService
import org.json.JSONObject
import java.lang.ref.WeakReference
-import java.util.*
+import java.util.LinkedList
import java.util.concurrent.ConcurrentHashMap
import kotlin.system.exitProcess
@@ -56,9 +56,10 @@ class WalletBackendService : Service() {
private val subscribers = LinkedList<Messenger>()
override fun onCreate() {
- val talerWalletAndroidCode =
assets.open("taler-wallet-android-$WALLET_CORE_VERSION.js").use {
- it.readBytes().toString(Charsets.UTF_8)
- }
+ val talerWalletAndroidCode =
+ assets.open("taler-wallet-android-$WALLET_CORE_VERSION.js").use {
+ it.readBytes().toString(Charsets.UTF_8)
+ }
Log.i(TAG, "onCreate in wallet backend service")
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 be50364..c090e75 100644
--- a/wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt
+++ b/wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt
@@ -28,7 +28,14 @@ import net.taler.common.Amount
import net.taler.wallet.R
import net.taler.wallet.balances.BalanceAdapter.BalanceViewHolder
-data class BalanceItem(val available: Amount, val pendingIncoming: Amount, val
hasPending: Boolean)
+data class BalanceItem(
+ val available: Amount,
+ val pendingIncoming: Amount,
+ val pendingOutgoing: Amount
+) {
+ val currency: String get() = available.currency
+ val hasPending: Boolean get() = !pendingIncoming.isZero() ||
!pendingOutgoing.isZero()
+}
class BalanceAdapter(private val listener: BalanceClickListener) :
Adapter<BalanceViewHolder>() {
@@ -65,7 +72,7 @@ class BalanceAdapter(private val listener:
BalanceClickListener) : Adapter<Balan
fun bind(item: BalanceItem) {
v.setOnClickListener {
listener.onBalanceClick(item.available.currency) }
- currencyView.text = item.available.currency
+ currencyView.text = item.currency
amountView.text = item.available.amountStr
val amountIncoming = item.pendingIncoming
diff --git a/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt
b/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt
index 22dd992..2b4d032 100644
--- a/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt
@@ -60,7 +60,7 @@ class BalancesFragment : Fragment(),
}
model.balances.observe(viewLifecycleOwner, Observer {
- onBalancesChanged(it.values.toList())
+ onBalancesChanged(it)
})
}
diff --git a/wallet/src/main/java/net/taler/wallet/crypto/Encoding.kt
b/wallet/src/main/java/net/taler/wallet/crypto/Encoding.kt
deleted file mode 100644
index 25a59be..0000000
--- a/wallet/src/main/java/net/taler/wallet/crypto/Encoding.kt
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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.wallet.crypto
-
-import java.io.ByteArrayOutputStream
-
-class EncodingException : Exception("Invalid encoding")
-
-
-object Base32Crockford {
-
- private fun ByteArray.getIntAt(index: Int): Int {
- val x = this[index].toInt()
- return if (x >= 0) x else (x + 256)
- }
-
- private var encTable = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
-
- fun encode(data: ByteArray): String {
- val sb = StringBuilder()
- val size = data.size
- var bitBuf = 0
- var numBits = 0
- var pos = 0
- while (pos < size || numBits > 0) {
- if (pos < size && numBits < 5) {
- val d = data.getIntAt(pos++)
- bitBuf = (bitBuf shl 8) or d
- numBits += 8
- }
- if (numBits < 5) {
- // zero-padding
- bitBuf = bitBuf shl (5 - numBits)
- numBits = 5
- }
- val v = bitBuf.ushr(numBits - 5) and 31
- sb.append(encTable[v])
- numBits -= 5
- }
- return sb.toString()
- }
-
- fun decode(encoded: String, out: ByteArrayOutputStream) {
- val size = encoded.length
- var bitpos = 0
- var bitbuf = 0
- var readPosition = 0
-
- while (readPosition < size || bitpos > 0) {
- //println("at position $readPosition with bitpos $bitpos")
- if (readPosition < size) {
- val v = getValue(encoded[readPosition++])
- bitbuf = (bitbuf shl 5) or v
- bitpos += 5
- }
- while (bitpos >= 8) {
- val d = (bitbuf ushr (bitpos - 8)) and 0xFF
- out.write(d)
- bitpos -= 8
- }
- if (readPosition == size && bitpos > 0) {
- bitbuf = (bitbuf shl (8 - bitpos)) and 0xFF
- bitpos = if (bitbuf == 0) 0 else 8
- }
- }
- }
-
- fun decode(encoded: String): ByteArray {
- val out = ByteArrayOutputStream()
- decode(encoded, out)
- return out.toByteArray()
- }
-
- private fun getValue(chr: Char): Int {
- var a = chr
- when (a) {
- 'O', 'o' -> a = '0'
- 'i', 'I', 'l', 'L' -> a = '1'
- 'u', 'U' -> a = 'V'
- }
- if (a in '0'..'9')
- return a - '0'
- if (a in 'a'..'z')
- a = Character.toUpperCase(a)
- var dec = 0
- if (a in 'A'..'Z') {
- if ('I' < a) dec++
- if ('L' < a) dec++
- if ('O' < a) dec++
- if ('U' < a) dec++
- return a - 'A' + 10 - dec
- }
- throw EncodingException()
- }
-
- /**
- * Compute the length of the resulting string when encoding data of the
given size
- * in bytes.
- *
- * @param dataSize size of the data to encode in bytes
- * @return size of the string that would result from encoding
- */
- @Suppress("unused")
- fun calculateEncodedStringLength(dataSize: Int): Int {
- return (dataSize * 8 + 4) / 5
- }
-
- /**
- * Compute the length of the resulting data in bytes when decoding a
(valid) string of the
- * given size.
- *
- * @param stringSize size of the string to decode
- * @return size of the resulting data in bytes
- */
- @Suppress("unused")
- fun calculateDecodedDataLength(stringSize: Int): Int {
- return stringSize * 5 / 8
- }
-}
-
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/ConfigResponse.kt
b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFees.kt
similarity index 54%
copy from merchant-lib/src/main/java/net/taler/merchantlib/ConfigResponse.kt
copy to wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFees.kt
index 49164e6..a026283 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/ConfigResponse.kt
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeFees.kt
@@ -14,21 +14,31 @@
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-package net.taler.merchantlib
+package net.taler.wallet.exchanges
-import kotlinx.serialization.Serializable
+import net.taler.common.Amount
+import net.taler.common.Timestamp
-@Serializable
-data class ConfigResponse(
- /**
- * libtool-style representation of the Merchant protocol version, see
- *
https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
- * The format is "current:revision:age".
- */
- val version: String,
+data class CoinFee(
+ val coin: Amount,
+ val quantity: Int,
+ val feeDeposit: Amount,
+ val feeRefresh: Amount,
+ val feeRefund: Amount,
+ val feeWithdraw: Amount
+)
+
+data class WireFee(
+ val start: Timestamp,
+ val end: Timestamp,
+ val wireFee: Amount,
+ val closingFee: Amount
+)
- /**
- Currency supported by this backend.
- */
- val currency: String
+data class ExchangeFees(
+ val withdrawFee: Amount,
+ val overhead: Amount,
+ val earliestDepositExpiration: Timestamp,
+ val coinFees: List<CoinFee>,
+ val wireFees: List<WireFee>
)
diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt
b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt
index 41c8f2c..9d31b5f 100644
--- a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt
@@ -21,7 +21,6 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
-import net.taler.common.Amount
import net.taler.common.Event
import net.taler.common.toEvent
import net.taler.wallet.TAG
@@ -46,7 +45,7 @@ class ExchangeManager(
private fun list(): LiveData<List<ExchangeItem>> {
mProgress.value = true
- walletBackendApi.sendRequest("listExchanges", JSONObject()) { isError,
result ->
+ walletBackendApi.sendRequest("listExchanges") { isError, result ->
if (isError) {
throw AssertionError("Wallet core failed to return exchanges!")
} else {
diff --git
a/wallet/src/main/java/net/taler/wallet/withdraw/SelectExchangeFragment.kt
b/wallet/src/main/java/net/taler/wallet/exchanges/SelectExchangeFragment.kt
similarity index 94%
rename from
wallet/src/main/java/net/taler/wallet/withdraw/SelectExchangeFragment.kt
rename to
wallet/src/main/java/net/taler/wallet/exchanges/SelectExchangeFragment.kt
index 2ade9f2..ef4894d 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/SelectExchangeFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/SelectExchangeFragment.kt
@@ -14,7 +14,7 @@
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-package net.taler.wallet.withdraw
+package net.taler.wallet.exchanges
import android.os.Bundle
import android.view.LayoutInflater
@@ -33,8 +33,8 @@ import net.taler.common.toRelativeTime
import net.taler.common.toShortDate
import net.taler.wallet.MainViewModel
import net.taler.wallet.R
-import net.taler.wallet.withdraw.CoinFeeAdapter.CoinFeeViewHolder
-import net.taler.wallet.withdraw.WireFeeAdapter.WireFeeViewHolder
+import net.taler.wallet.exchanges.CoinFeeAdapter.CoinFeeViewHolder
+import net.taler.wallet.exchanges.WireFeeAdapter.WireFeeViewHolder
class SelectExchangeFragment : Fragment() {
@@ -59,8 +59,10 @@ class SelectExchangeFragment : Fragment() {
overheadView.visibility = GONE
} else overheadView.setAmount(fees.overhead)
expirationView.text =
fees.earliestDepositExpiration.ms.toRelativeTime(requireContext())
- coinFeesList.adapter = CoinFeeAdapter(fees.coinFees)
- wireFeesList.adapter = WireFeeAdapter(fees.wireFees)
+ coinFeesList.adapter =
+ CoinFeeAdapter(fees.coinFees)
+ wireFeesList.adapter =
+ WireFeeAdapter(fees.wireFees)
}
private fun TextView.setAmount(amount: Amount) {
diff --git a/wallet/src/main/java/net/taler/wallet/history/DevHistoryAdapter.kt
b/wallet/src/main/java/net/taler/wallet/history/DevHistoryAdapter.kt
deleted file mode 100644
index a2684e1..0000000
--- a/wallet/src/main/java/net/taler/wallet/history/DevHistoryAdapter.kt
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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.wallet.history
-
-import android.content.Context
-import android.view.LayoutInflater
-import android.view.View
-import android.view.View.GONE
-import android.view.View.VISIBLE
-import android.view.ViewGroup
-import android.widget.ImageView
-import android.widget.TextView
-import androidx.recyclerview.widget.RecyclerView.Adapter
-import androidx.recyclerview.widget.RecyclerView.ViewHolder
-import net.taler.common.exhaustive
-import net.taler.common.toRelativeTime
-import net.taler.wallet.R
-import net.taler.wallet.history.DevHistoryAdapter.HistoryViewHolder
-import net.taler.wallet.transactions.AmountType
-
-internal class DevHistoryAdapter(
- private val listener: OnEventClickListener
-) : Adapter<HistoryViewHolder>() {
-
- private var history: List<HistoryEvent> = ArrayList()
-
- init {
- setHasStableIds(false)
- }
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
HistoryViewHolder {
- val view = LayoutInflater.from(parent.context)
- .inflate(R.layout.list_item_history, parent, false)
- return HistoryViewHolder(view)
- }
-
- override fun getItemCount(): Int = history.size
-
- override fun onBindViewHolder(holder: HistoryViewHolder, position: Int) {
- val transaction = history[position]
- holder.bind(transaction)
- }
-
- fun update(updatedHistory: List<HistoryEvent>) {
- this.history = updatedHistory
- this.notifyDataSetChanged()
- }
-
- internal open inner class HistoryViewHolder(private val v: View) :
ViewHolder(v) {
-
- protected val context: Context = v.context
-
- private val icon: ImageView = v.findViewById(R.id.icon)
- protected val title: TextView = v.findViewById(R.id.title)
- private val time: TextView = v.findViewById(R.id.time)
- private val amount: TextView = v.findViewById(R.id.amount)
-
- private val amountColor = amount.currentTextColor
-
- open fun bind(historyEvent: HistoryEvent) {
- v.setOnClickListener { listener.onTransactionClicked(historyEvent)
}
- icon.setImageResource(historyEvent.icon)
- title.text = historyEvent.title
- time.text = historyEvent.timestamp.ms.toRelativeTime(context)
- bindAmount(historyEvent.displayAmount)
- }
-
- private fun bindAmount(displayAmount: DisplayAmount?) {
- if (displayAmount == null) {
- amount.visibility = GONE
- } else {
- amount.visibility = VISIBLE
- when (displayAmount.type) {
- AmountType.Positive -> {
- amount.text = context.getString(
- R.string.amount_positive,
displayAmount.amount.amountStr
- )
- amount.setTextColor(context.getColor(R.color.green))
- }
- AmountType.Negative -> {
- amount.text = context.getString(
- R.string.amount_negative,
displayAmount.amount.amountStr
- )
- amount.setTextColor(context.getColor(R.color.red))
- }
- AmountType.Neutral -> {
- amount.text = displayAmount.amount.amountStr
- amount.setTextColor(amountColor)
- }
- }.exhaustive
- }
- }
-
- }
-
-}
diff --git
a/wallet/src/main/java/net/taler/wallet/history/DevHistoryFragment.kt
b/wallet/src/main/java/net/taler/wallet/history/DevHistoryFragment.kt
deleted file mode 100644
index c3c07a3..0000000
--- a/wallet/src/main/java/net/taler/wallet/history/DevHistoryFragment.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.wallet.history
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.View.INVISIBLE
-import android.view.View.VISIBLE
-import android.view.ViewGroup
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Observer
-import androidx.recyclerview.widget.DividerItemDecoration
-import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL
-import kotlinx.android.synthetic.main.fragment_transactions.*
-import net.taler.common.fadeIn
-import net.taler.common.fadeOut
-import net.taler.wallet.MainViewModel
-import net.taler.wallet.R
-
-internal interface OnEventClickListener {
- fun onTransactionClicked(historyEvent: HistoryEvent)
-}
-
-class DevHistoryFragment : Fragment(),
- OnEventClickListener {
-
- private val model: MainViewModel by activityViewModels()
- private val historyManager by lazy { model.historyManager }
- private val historyAdapter by lazy { DevHistoryAdapter(this) }
-
- override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- return inflater.inflate(R.layout.fragment_transactions, container,
false)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- if (savedInstanceState == null) historyManager.loadHistory()
-
- list.apply {
- adapter = historyAdapter
- addItemDecoration(DividerItemDecoration(context, VERTICAL))
- }
- historyManager.progress.observe(viewLifecycleOwner, Observer { show ->
- progressBar.visibility = if (show) VISIBLE else INVISIBLE
- })
- historyManager.history.observe(viewLifecycleOwner, Observer { result ->
- onHistoryResult(result)
- })
- }
-
- override fun onTransactionClicked(historyEvent: HistoryEvent) {
- JsonDialogFragment.new(historyEvent.json.toString(2))
- .show(parentFragmentManager, null)
- }
-
- private fun onHistoryResult(result: HistoryResult) = when (result) {
- HistoryResult.Error -> {
- list.fadeOut()
- emptyState.text = getString(R.string.transactions_error)
- emptyState.fadeIn()
- }
- is HistoryResult.Success -> {
- emptyState.visibility = if (result.history.isEmpty()) VISIBLE else
INVISIBLE
- historyAdapter.update(result.history)
- list.fadeIn()
- }
- }
-
-}
diff --git a/wallet/src/main/java/net/taler/wallet/history/DevHistoryManager.kt
b/wallet/src/main/java/net/taler/wallet/history/DevHistoryManager.kt
deleted file mode 100644
index 9052d6e..0000000
--- a/wallet/src/main/java/net/taler/wallet/history/DevHistoryManager.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.wallet.history
-
-import androidx.annotation.UiThread
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.module.kotlin.readValue
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import net.taler.wallet.backend.WalletBackendApi
-import org.json.JSONObject
-import java.util.*
-
-sealed class HistoryResult {
- object Error : HistoryResult()
- class Success(val history: List<HistoryEvent>) : HistoryResult()
-}
-
-class DevHistoryManager(
- private val walletBackendApi: WalletBackendApi,
- private val scope: CoroutineScope,
- private val mapper: ObjectMapper
-) {
-
- private val mProgress = MutableLiveData<Boolean>()
- val progress: LiveData<Boolean> = mProgress
-
- private val mHistory = MutableLiveData<HistoryResult>()
- val history: LiveData<HistoryResult> = mHistory
-
- @UiThread
- internal fun loadHistory() {
- mProgress.value = true
- walletBackendApi.sendRequest("getHistory", null) { isError, result ->
- scope.launch(Dispatchers.Default) {
- onEventsLoaded(isError, result)
- }
- }
- }
-
- private fun onEventsLoaded(isError: Boolean, result: JSONObject) {
- if (isError) {
- mHistory.postValue(HistoryResult.Error)
- return
- }
- val history = LinkedList<HistoryEvent>()
- val json = result.getJSONArray("history")
- for (i in 0 until json.length()) {
- val event: HistoryEvent = mapper.readValue(json.getString(i))
- event.json = json.getJSONObject(i)
- history.add(event)
- }
- history.reverse() // show latest first
- mProgress.postValue(false)
- mHistory.postValue(HistoryResult.Success(history))
- }
-
-}
diff --git a/wallet/src/main/java/net/taler/wallet/history/HistoryEvent.kt
b/wallet/src/main/java/net/taler/wallet/history/HistoryEvent.kt
deleted file mode 100644
index 3cbe7d7..0000000
--- a/wallet/src/main/java/net/taler/wallet/history/HistoryEvent.kt
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * 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.wallet.history
-
-import androidx.annotation.DrawableRes
-import com.fasterxml.jackson.annotation.JsonSubTypes
-import com.fasterxml.jackson.annotation.JsonSubTypes.Type
-import com.fasterxml.jackson.annotation.JsonTypeInfo
-import com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY
-import com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME
-import com.fasterxml.jackson.annotation.JsonTypeName
-import net.taler.common.Amount
-import net.taler.common.Timestamp
-import net.taler.wallet.R
-import net.taler.wallet.transactions.AmountType
-import org.json.JSONObject
-
-class DisplayAmount(
- val amount: Amount,
- val type: AmountType
-)
-
-@JsonTypeInfo(
- use = NAME,
- include = PROPERTY,
- property = "type",
- defaultImpl = UnknownHistoryEvent::class
-)
-/** missing:
-AuditorComplaintSent = "auditor-complained-sent",
-AuditorComplaintProcessed = "auditor-complaint-processed",
-AuditorTrustAdded = "auditor-trust-added",
-AuditorTrustRemoved = "auditor-trust-removed",
-ExchangeTermsAccepted = "exchange-terms-accepted",
-ExchangePolicyChanged = "exchange-policy-changed",
-ExchangeTrustAdded = "exchange-trust-added",
-ExchangeTrustRemoved = "exchange-trust-removed",
-FundsDepositedToSelf = "funds-deposited-to-self",
-FundsRecouped = "funds-recouped",
-ReserveCreated = "reserve-created",
- */
-@JsonSubTypes(
- Type(value = ExchangeAddedEvent::class, name = "exchange-added"),
- Type(value = ExchangeUpdatedEvent::class, name = "exchange-updated"),
- Type(value = ReserveBalanceUpdatedHistoryEvent::class, name =
"reserve-balance-updated"),
- Type(value = WithdrawHistoryEvent::class, name = "withdrawn"),
- Type(value = OrderAcceptedHistoryEvent::class, name = "order-accepted"),
- Type(value = OrderRefusedHistoryEvent::class, name = "order-refused"),
- Type(value = OrderRedirectedHistoryEvent::class, name =
"order-redirected"),
- Type(value = PaymentHistoryEvent::class, name = "payment-sent"),
- Type(value = PaymentAbortedHistoryEvent::class, name = "payment-aborted"),
- Type(value = TipAcceptedHistoryEvent::class, name = "tip-accepted"),
- Type(value = TipDeclinedHistoryEvent::class, name = "tip-declined"),
- Type(value = RefundHistoryEvent::class, name = "refund"),
- Type(value = RefreshHistoryEvent::class, name = "refreshed")
-)
-abstract class HistoryEvent(
- val timestamp: Timestamp,
- val eventId: String,
- @get:DrawableRes
- open val icon: Int = R.drawable.ic_account_balance
-) {
- val title: String get() = this::class.java.simpleName
- open val displayAmount: DisplayAmount? = null
- lateinit var json: JSONObject
-}
-
-class UnknownHistoryEvent(timestamp: Timestamp, eventId: String) :
HistoryEvent(timestamp, eventId)
-
-@JsonTypeName("exchange-added")
-class ExchangeAddedEvent(
- timestamp: Timestamp,
- eventId: String
-) : HistoryEvent(timestamp, eventId)
-
-@JsonTypeName("exchange-updated")
-class ExchangeUpdatedEvent(
- timestamp: Timestamp,
- eventId: String
-) : HistoryEvent(timestamp, eventId)
-
-@JsonTypeName("reserve-balance-updated")
-class ReserveBalanceUpdatedHistoryEvent(
- timestamp: Timestamp,
- eventId: String,
- val reserveBalance: Amount
-) : HistoryEvent(timestamp, eventId) {
- override val displayAmount = DisplayAmount(reserveBalance,
AmountType.Neutral)
-}
-
-@JsonTypeName("withdrawn")
-class WithdrawHistoryEvent(
- timestamp: Timestamp,
- eventId: String,
- val amountWithdrawnEffective: Amount
-) : HistoryEvent(timestamp, eventId) {
- override val icon = R.drawable.transaction_withdrawal
- override val displayAmount = DisplayAmount(amountWithdrawnEffective,
AmountType.Positive)
-}
-
-@JsonTypeName("order-accepted")
-class OrderAcceptedHistoryEvent(
- timestamp: Timestamp,
- eventId: String
-) : HistoryEvent(timestamp, eventId) {
- override val icon = R.drawable.ic_add_circle
-}
-
-@JsonTypeName("order-refused")
-class OrderRefusedHistoryEvent(
- timestamp: Timestamp,
- eventId: String
-) : HistoryEvent(timestamp, eventId) {
- override val icon = R.drawable.ic_cancel
-}
-
-@JsonTypeName("payment-sent")
-class PaymentHistoryEvent(
- timestamp: Timestamp,
- eventId: String,
- val amountPaidWithFees: Amount
-) : HistoryEvent(timestamp, eventId) {
- override val icon = R.drawable.ic_cash_usd_outline
- override val displayAmount = DisplayAmount(amountPaidWithFees,
AmountType.Negative)
-}
-
-@JsonTypeName("payment-aborted")
-class PaymentAbortedHistoryEvent(
- timestamp: Timestamp,
- eventId: String,
- amountLost: Amount
-) : HistoryEvent(timestamp, eventId) {
- override val icon = R.drawable.transaction_payment_aborted
- override val displayAmount = DisplayAmount(amountLost, AmountType.Negative)
-}
-
-@JsonTypeName("refreshed")
-class RefreshHistoryEvent(
- timestamp: Timestamp,
- eventId: String,
- val amountRefreshedEffective: Amount,
- val amountRefreshedRaw: Amount
-) : HistoryEvent(timestamp, eventId) {
- override val icon = R.drawable.transaction_refresh
- override val displayAmount =
- DisplayAmount(amountRefreshedRaw - amountRefreshedEffective,
AmountType.Negative)
-}
-
-@JsonTypeName("order-redirected")
-class OrderRedirectedHistoryEvent(
- timestamp: Timestamp,
- eventId: String
-) : HistoryEvent(timestamp, eventId) {
- override val icon = R.drawable.ic_directions
-}
-
-@JsonTypeName("tip-accepted")
-class TipAcceptedHistoryEvent(
- timestamp: Timestamp,
- eventId: String,
- tipRaw: Amount
-) : HistoryEvent(timestamp, eventId) {
- override val icon = R.drawable.transaction_tip_accepted
- override val displayAmount = DisplayAmount(tipRaw, AmountType.Positive)
-}
-
-@JsonTypeName("tip-declined")
-class TipDeclinedHistoryEvent(
- timestamp: Timestamp,
- eventId: String,
- tipAmount: Amount
-) : HistoryEvent(timestamp, eventId) {
- override val icon = R.drawable.transaction_tip_declined
- override val displayAmount = DisplayAmount(tipAmount, AmountType.Neutral)
-}
-
-@JsonTypeName("refund")
-class RefundHistoryEvent(
- timestamp: Timestamp,
- eventId: String,
- val amountRefundedEffective: Amount
-) : HistoryEvent(timestamp, eventId) {
- override val icon = R.drawable.transaction_refund
- override val displayAmount = DisplayAmount(amountRefundedEffective,
AmountType.Positive)
-}
diff --git
a/wallet/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt
b/wallet/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt
deleted file mode 100644
index 31c2b93..0000000
--- a/wallet/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.wallet.history
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import androidx.fragment.app.DialogFragment
-import kotlinx.android.synthetic.main.fragment_json.*
-import net.taler.wallet.R
-
-class JsonDialogFragment : DialogFragment() {
-
- companion object {
- fun new(json: String): JsonDialogFragment {
- return JsonDialogFragment().apply {
- arguments = Bundle().apply { putString("json", json) }
- }
- }
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- return inflater.inflate(R.layout.fragment_json, container, false)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- val json = requireArguments().getString("json")
- jsonView.text = json
- }
-
- override fun onStart() {
- super.onStart()
- dialog?.window?.setLayout(MATCH_PARENT, WRAP_CONTENT)
- }
-
-}
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 5c73d6c..2427afb 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
@@ -26,11 +26,32 @@ import net.taler.common.Amount
import net.taler.common.ContractTerms
import net.taler.wallet.TAG
import net.taler.wallet.backend.WalletBackendApi
+import net.taler.wallet.payment.PayStatus.AlreadyPaid
+import net.taler.wallet.payment.PayStatus.InsufficientBalance
+import net.taler.wallet.payment.PreparePayResponse.AlreadyConfirmedResponse
+import net.taler.wallet.payment.PreparePayResponse.InsufficientBalanceResponse
+import net.taler.wallet.payment.PreparePayResponse.PaymentPossibleResponse
import org.json.JSONObject
import java.net.MalformedURLException
val REGEX_PRODUCT_IMAGE =
Regex("^data:image/(jpeg|png);base64,([A-Za-z0-9+/=]+)$")
+sealed class PayStatus {
+ object None : PayStatus()
+ object Loading : PayStatus()
+ data class Prepared(
+ val contractTerms: ContractTerms,
+ val proposalId: String,
+ val amountRaw: Amount,
+ val amountEffective: Amount
+ ) : PayStatus()
+
+ data class InsufficientBalance(val contractTerms: ContractTerms) :
PayStatus()
+ object AlreadyPaid : PayStatus()
+ data class Error(val error: String) : PayStatus()
+ data class Success(val currency: String) : PayStatus()
+}
+
class PaymentManager(
private val walletBackendApi: WalletBackendApi,
private val mapper: ObjectMapper
@@ -42,52 +63,27 @@ class PaymentManager(
private val mDetailsShown = MutableLiveData<Boolean>()
internal val detailsShown: LiveData<Boolean> = mDetailsShown
- private var currentPayRequestId = 0
-
@UiThread
fun preparePay(url: String) {
mPayStatus.value = PayStatus.Loading
mDetailsShown.value = false
- val args = JSONObject(mapOf("url" to url))
-
- currentPayRequestId += 1
- val payRequestId = currentPayRequestId
-
+ val args = JSONObject(mapOf("talerPayUri" to url))
walletBackendApi.sendRequest("preparePay", args) { isError, result ->
- when {
- isError -> {
- Log.v(TAG, "got preparePay error result")
- mPayStatus.value = PayStatus.Error(result.toString())
- }
- payRequestId != this.currentPayRequestId -> {
- Log.v(TAG, "preparePay result was for old request")
- }
- else -> {
- val status = result.getString("status")
- try {
- mPayStatus.postValue(getPayStatusUpdate(status,
result))
- } catch (e: Exception) {
- Log.e(TAG, "Error getting PayStatusUpdate", e)
- mPayStatus.postValue(PayStatus.Error(e.message ?:
"unknown error"))
- }
- }
+ if (isError) {
+ handleError("preparePay", result.toString(2))
+ return@sendRequest
+ }
+ val response: PreparePayResponse =
mapper.readValue(result.toString())
+ Log.e(TAG, "PreparePayResponse $response")
+ mPayStatus.value = when (response) {
+ is PaymentPossibleResponse -> response.toPayStatusPrepared()
+ is InsufficientBalanceResponse ->
InsufficientBalance(response.contractTerms)
+ is AlreadyConfirmedResponse -> AlreadyPaid
}
}
}
- private fun getPayStatusUpdate(status: String, json: JSONObject) = when
(status) {
- "payment-possible" -> PayStatus.Prepared(
- contractTerms = getContractTerms(json),
- proposalId = json.getString("proposalId"),
- totalFees = Amount.fromJsonObject(json.getJSONObject("totalFees"))
- )
- "paid" -> PayStatus.AlreadyPaid(getContractTerms(json))
- "insufficient-balance" ->
PayStatus.InsufficientBalance(getContractTerms(json))
- "error" -> PayStatus.Error("got some error")
- else -> PayStatus.Error("unknown status")
- }
-
private fun getContractTerms(json: JSONObject): ContractTerms {
val terms: ContractTerms =
mapper.readValue(json.getString("contractTermsRaw"))
// validate product images
@@ -101,16 +97,13 @@ class PaymentManager(
return terms
}
- @UiThread
- fun toggleDetailsShown() {
- val oldValue = mDetailsShown.value ?: false
- mDetailsShown.value = !oldValue
- }
-
fun confirmPay(proposalId: String, currency: String) {
val args = JSONObject(mapOf("proposalId" to proposalId))
-
- walletBackendApi.sendRequest("confirmPay", args) { _, _ ->
+ walletBackendApi.sendRequest("confirmPay", args) { isError, result ->
+ if (isError) {
+ handleError("preparePay", result.toString())
+ return@sendRequest
+ }
mPayStatus.postValue(PayStatus.Success(currency))
}
}
@@ -129,8 +122,9 @@ class PaymentManager(
Log.i(TAG, "aborting proposal")
- walletBackendApi.sendRequest("abortProposal", args) { isError, _ ->
+ walletBackendApi.sendRequest("abortProposal", args) { isError, result
->
if (isError) {
+ handleError("abortProposal", result.toString(2))
Log.e(TAG, "received error response to abortProposal")
return@sendRequest
}
@@ -138,24 +132,20 @@ class PaymentManager(
}
}
+ @UiThread
+ fun toggleDetailsShown() {
+ val oldValue = mDetailsShown.value ?: false
+ mDetailsShown.value = !oldValue
+ }
+
@UiThread
fun resetPayStatus() {
mPayStatus.value = PayStatus.None
}
-}
-
-sealed class PayStatus {
- object None : PayStatus()
- object Loading : PayStatus()
- data class Prepared(
- val contractTerms: ContractTerms,
- val proposalId: String,
- val totalFees: Amount
- ) : PayStatus()
+ private fun handleError(operation: String, msg: String) {
+ Log.e(TAG, "got $operation error result $msg")
+ mPayStatus.value = PayStatus.Error(msg)
+ }
- data class InsufficientBalance(val contractTerms: ContractTerms) :
PayStatus()
- data class AlreadyPaid(val contractTerms: ContractTerms) : PayStatus()
- data class Error(val error: String) : PayStatus()
- data class Success(val currency: String) : PayStatus()
}
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PaymentResponses.kt
b/wallet/src/main/java/net/taler/wallet/payment/PaymentResponses.kt
new file mode 100644
index 0000000..1ff8867
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/payment/PaymentResponses.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.wallet.payment
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo
+import com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME
+import com.fasterxml.jackson.annotation.JsonTypeName
+import net.taler.common.Amount
+import net.taler.common.ContractTerms
+
+@JsonTypeInfo(use = NAME, property = "status")
+sealed class PreparePayResponse(open val proposalId: String) {
+ @JsonTypeName("payment-possible")
+ data class PaymentPossibleResponse(
+ override val proposalId: String,
+ val amountRaw: Amount,
+ val amountEffective: Amount,
+ val contractTerms: ContractTerms
+ ) : PreparePayResponse(proposalId) {
+ fun toPayStatusPrepared() = PayStatus.Prepared(
+ contractTerms = contractTerms,
+ proposalId = proposalId,
+ amountRaw = amountRaw,
+ amountEffective = amountEffective
+ )
+ }
+
+ @JsonTypeName("insufficient-balance")
+ data class InsufficientBalanceResponse(
+ override val proposalId: String,
+ val contractTerms: ContractTerms
+ ) : PreparePayResponse(proposalId)
+
+ @JsonTypeName("already-confirmed")
+ data class AlreadyConfirmedResponse(
+ override val proposalId: String,
+ /**
+ * Did the payment succeed?
+ */
+ val paid: Boolean,
+
+ /**
+ * Redirect URL for the fulfillment page, only given if paid==true.
+ */
+ val nextUrl: String?
+ ) : PreparePayResponse(proposalId)
+}
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 6f806b7..ce2b6f7 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
@@ -95,7 +95,8 @@ class PromptPaymentFragment : Fragment(),
ProductImageClickListener {
when (payStatus) {
is PayStatus.Prepared -> {
showLoading(false)
- showOrder(payStatus.contractTerms, payStatus.totalFees)
+ val fees = payStatus.amountEffective - payStatus.amountRaw
+ showOrder(payStatus.contractTerms, fees)
confirmButton.isEnabled = true
confirmButton.setOnClickListener {
model.showProgressBar.value = true
@@ -109,7 +110,7 @@ class PromptPaymentFragment : Fragment(),
ProductImageClickListener {
}
is PayStatus.InsufficientBalance -> {
showLoading(false)
- showOrder(payStatus.contractTerms, null)
+ showOrder(payStatus.contractTerms)
errorView.setText(R.string.payment_balance_insufficient)
errorView.fadeIn()
}
@@ -141,7 +142,7 @@ class PromptPaymentFragment : Fragment(),
ProductImageClickListener {
}
}
- private fun showOrder(contractTerms: ContractTerms, totalFees: Amount?) {
+ private fun showOrder(contractTerms: ContractTerms, totalFees: Amount? =
null) {
orderView.text = contractTerms.summary
adapter.setItems(contractTerms.products)
if (contractTerms.products.size == 1)
paymentManager.toggleDetailsShown()
diff --git
a/wallet/src/main/java/net/taler/wallet/pending/PendingOperationsManager.kt
b/wallet/src/main/java/net/taler/wallet/pending/PendingOperationsManager.kt
index 6c58b81..7027687 100644
--- a/wallet/src/main/java/net/taler/wallet/pending/PendingOperationsManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/pending/PendingOperationsManager.kt
@@ -32,7 +32,7 @@ class PendingOperationsManager(private val walletBackendApi:
WalletBackendApi) {
val pendingOperations = MutableLiveData<List<PendingOperationInfo>>()
internal fun getPending() {
- walletBackendApi.sendRequest("getPendingOperations", null) { isError,
result ->
+ walletBackendApi.sendRequest("getPendingOperations") { isError, result
->
if (isError) {
Log.i(TAG, "got getPending error result: $result")
return@sendRequest
@@ -51,7 +51,7 @@ class PendingOperationsManager(private val walletBackendApi:
WalletBackendApi) {
}
fun retryPendingNow() {
- walletBackendApi.sendRequest("retryPendingNow", null)
+ walletBackendApi.sendRequest("retryPendingNow")
}
}
diff --git
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
index d8204b6..bd37b37 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
@@ -102,12 +102,4 @@ class TransactionManager(
}
}
- @UiThread
- fun hasPending(currency: String): Boolean {
- val result = mTransactions[currency]?.value ?: return false
- return if (result is TransactionsResult.Success) {
- result.transactions.any { it.pending }
- } else false
- }
-
}
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 526aa94..2ae58c3 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt
@@ -112,7 +112,7 @@ class TransactionsFragment : Fragment(),
OnTransactionClickListener, ActionMode.
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
model.balances.observe(viewLifecycleOwner, Observer { balances ->
- balances[currency]?.available?.let { amount ->
+ balances.find { it.currency == currency }?.available?.let { amount
->
requireActivity().title =
getString(R.string.transactions_detail_title_balance,
amount)
}
diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/ExchangeFees.kt
b/wallet/src/main/java/net/taler/wallet/withdraw/ExchangeFees.kt
deleted file mode 100644
index 9c815c9..0000000
--- a/wallet/src/main/java/net/taler/wallet/withdraw/ExchangeFees.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.wallet.withdraw
-
-import net.taler.common.Amount
-import net.taler.common.Timestamp
-import org.json.JSONObject
-
-data class CoinFee(
- val coin: Amount,
- val quantity: Int,
- val feeDeposit: Amount,
- val feeRefresh: Amount,
- val feeRefund: Amount,
- val feeWithdraw: Amount
-)
-
-data class WireFee(
- val start: Timestamp,
- val end: Timestamp,
- val wireFee: Amount,
- val closingFee: Amount
-)
-
-data class ExchangeFees(
- val withdrawFee: Amount,
- val overhead: Amount,
- val earliestDepositExpiration: Timestamp,
- val coinFees: List<CoinFee>,
- val wireFees: List<WireFee>
-) {
- companion object {
- fun fromExchangeWithdrawDetailsJson(json: JSONObject): ExchangeFees {
- val earliestDepositExpiration =
- json.getJSONObject("earliestDepositExpiration").getLong("t_ms")
- val selectedDenoms = json.getJSONObject("selectedDenoms")
- val denoms = selectedDenoms.getJSONArray("selectedDenoms")
- val coinFees = ArrayList<CoinFee>(denoms.length())
- for (i in 0 until denoms.length()) {
- val denom = denoms.getJSONObject(i)
- val d = denom.getJSONObject("denom")
- val coinFee = CoinFee(
- coin = Amount.fromJsonObject(d.getJSONObject("value")),
- quantity = denom.getInt("count"),
- feeDeposit =
Amount.fromJsonObject(d.getJSONObject("feeDeposit")),
- feeRefresh =
Amount.fromJsonObject(d.getJSONObject("feeRefresh")),
- feeRefund =
Amount.fromJsonObject(d.getJSONObject("feeRefund")),
- feeWithdraw =
Amount.fromJsonObject(d.getJSONObject("feeWithdraw"))
- )
- coinFees.add(coinFee)
- }
-
- val wireFeesJson = json.getJSONObject("wireFees")
- val feesForType = wireFeesJson.getJSONObject("feesForType")
- val bankFees = feesForType.getJSONArray("x-taler-bank")
- val wireFees = ArrayList<WireFee>(bankFees.length())
- for (i in 0 until bankFees.length()) {
- val fee = bankFees.getJSONObject(i)
- val startStamp =
- fee.getJSONObject("startStamp").getLong("t_ms")
- val endStamp =
- fee.getJSONObject("endStamp").getLong("t_ms")
- val wireFee = WireFee(
- start = Timestamp(startStamp),
- end = Timestamp(endStamp),
- wireFee =
Amount.fromJsonObject(fee.getJSONObject("wireFee")),
- closingFee =
Amount.fromJsonObject(fee.getJSONObject("closingFee"))
- )
- wireFees.add(wireFee)
- }
-
- return ExchangeFees(
- withdrawFee =
Amount.fromJsonObject(json.getJSONObject("withdrawFee")),
- overhead =
Amount.fromJsonObject(json.getJSONObject("overhead")),
- earliestDepositExpiration =
Timestamp(earliestDepositExpiration),
- coinFees = coinFees,
- wireFees = wireFees
- )
- }
- }
-}
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 55f931d..9788d1c 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/ManualWithdrawFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/withdraw/ManualWithdrawFragment.kt
@@ -60,7 +60,7 @@ class ManualWithdrawFragment : Fragment() {
val amount = Amount(exchangeItem.currency, value, 0)
amountView.hideKeyboard()
Toast.makeText(view.context, "Not implemented: $amount",
LENGTH_SHORT).show()
- withdrawManager.getWithdrawalDetails(exchangeItem, amount)
+ withdrawManager.getWithdrawalDetails(exchangeItem.exchangeBaseUrl,
amount)
}
}
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 331554b..5a98a89 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt
@@ -20,6 +20,8 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.Toast
+import android.widget.Toast.LENGTH_SHORT
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
@@ -34,7 +36,7 @@ import net.taler.wallet.MainViewModel
import net.taler.wallet.R
import net.taler.wallet.cleanExchange
import net.taler.wallet.withdraw.WithdrawStatus.Loading
-import net.taler.wallet.withdraw.WithdrawStatus.TermsOfServiceReviewRequired
+import net.taler.wallet.withdraw.WithdrawStatus.TosReviewRequired
import net.taler.wallet.withdraw.WithdrawStatus.Withdrawing
class PromptWithdrawFragment : Fragment() {
@@ -59,17 +61,13 @@ class PromptWithdrawFragment : Fragment() {
private fun showWithdrawStatus(status: WithdrawStatus?): Any = when
(status) {
is WithdrawStatus.ReceivedDetails -> {
- showContent(status.amount, status.fee, status.exchange)
+ showContent(status.amountRaw, status.amountEffective,
status.exchangeBaseUrl)
confirmWithdrawButton.apply {
text = getString(R.string.withdraw_button_confirm)
setOnClickListener {
it.fadeOut()
confirmProgressBar.fadeIn()
- withdrawManager.acceptWithdrawal(
- status.talerWithdrawUri,
- status.exchange,
- status.amount.currency
- )
+ withdrawManager.acceptWithdrawal()
}
isEnabled = true
}
@@ -87,8 +85,8 @@ class PromptWithdrawFragment : Fragment() {
is Withdrawing -> {
model.showProgressBar.value = true
}
- is TermsOfServiceReviewRequired -> {
- showContent(status.amount, status.fee, status.exchange)
+ is TosReviewRequired -> {
+ showContent(status.amountRaw, status.amountEffective,
status.exchangeBaseUrl)
confirmWithdrawButton.apply {
text = getString(R.string.withdraw_button_tos)
setOnClickListener {
@@ -104,20 +102,20 @@ class PromptWithdrawFragment : Fragment() {
null -> model.showProgressBar.value = false
}
- private fun showContent(amount: Amount, fee: Amount, exchange: String) {
+ private fun showContent(amountRaw: Amount, amountEffective: Amount,
exchange: String) {
model.showProgressBar.value = false
progressBar.fadeOut()
introView.fadeIn()
- effectiveAmountView.text = (amount - fee).toString()
+ effectiveAmountView.text = amountEffective.toString()
effectiveAmountView.fadeIn()
chosenAmountLabel.fadeIn()
- chosenAmountView.text = amount.toString()
+ chosenAmountView.text = amountRaw.toString()
chosenAmountView.fadeIn()
feeLabel.fadeIn()
- feeView.text = getString(R.string.amount_negative, fee.toString())
+ feeView.text = getString(R.string.amount_negative, (amountRaw -
amountEffective).toString())
feeView.fadeIn()
exchangeIntroView.fadeIn()
@@ -125,7 +123,7 @@ class PromptWithdrawFragment : Fragment() {
withdrawExchangeUrl.fadeIn()
selectExchangeButton.fadeIn()
selectExchangeButton.setOnClickListener {
-
findNavController().navigate(R.id.action_promptWithdraw_to_selectExchangeFragment)
+ Toast.makeText(context, "Not yet implemented", LENGTH_SHORT).show()
}
withdrawCard.fadeIn()
diff --git
a/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt
b/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt
index ffaef5a..db1f326 100644
---
a/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt
+++
b/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt
@@ -55,7 +55,7 @@ class ReviewExchangeTosFragment : Fragment() {
}
withdrawManager.withdrawStatus.observe(viewLifecycleOwner, Observer {
when (it) {
- is WithdrawStatus.TermsOfServiceReviewRequired -> {
+ is WithdrawStatus.TosReviewRequired -> {
val sections = try {
// TODO remove next line once exchange delivers proper
markdown
val text = it.tosText.replace("****************",
"================")
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 ea65e7c..e14a747 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
@@ -17,38 +17,57 @@
package net.taler.wallet.withdraw
import android.util.Log
+import androidx.annotation.UiThread
import androidx.lifecycle.MutableLiveData
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.kotlin.readValue
import net.taler.common.Amount
import net.taler.wallet.TAG
import net.taler.wallet.backend.WalletBackendApi
+import net.taler.wallet.exchanges.ExchangeFees
import net.taler.wallet.exchanges.ExchangeItem
import net.taler.wallet.withdraw.WithdrawStatus.ReceivedDetails
import org.json.JSONObject
sealed class WithdrawStatus {
- data class Loading(val talerWithdrawUri: String) : WithdrawStatus()
- data class TermsOfServiceReviewRequired(
- val talerWithdrawUri: String,
- val exchange: String,
+ data class Loading(val talerWithdrawUri: String? = null) : WithdrawStatus()
+ data class TosReviewRequired(
+ val talerWithdrawUri: String? = null,
+ val exchangeBaseUrl: String,
+ val amountRaw: Amount,
+ val amountEffective: Amount,
val tosText: String,
- val tosEtag: String,
- val amount: Amount,
- val fee: Amount
+ val tosEtag: String
) : WithdrawStatus()
data class ReceivedDetails(
- val talerWithdrawUri: String,
- val exchange: String,
- val amount: Amount,
- val fee: Amount
+ val talerWithdrawUri: String? = null,
+ val exchangeBaseUrl: String,
+ val amountRaw: Amount,
+ val amountEffective: Amount
) : WithdrawStatus()
- data class Withdrawing(val talerWithdrawUri: String) : WithdrawStatus()
+ object Withdrawing : WithdrawStatus()
data class Success(val currency: String) : WithdrawStatus()
data class Error(val message: String?) : WithdrawStatus()
}
-class WithdrawManager(private val walletBackendApi: WalletBackendApi) {
+data class WithdrawalDetailsForUri(
+ val amount: Amount,
+ val defaultExchangeBaseUrl: String?,
+ val possibleExchanges: List<ExchangeItem>
+)
+
+data class WithdrawalDetails(
+ val tosAccepted: Boolean,
+ val amountRaw: Amount,
+ val amountEffective: Amount
+)
+
+class WithdrawManager(
+ private val walletBackendApi: WalletBackendApi,
+ private val mapper: ObjectMapper
+) {
val withdrawStatus = MutableLiveData<WithdrawStatus>()
val testWithdrawalInProgress = MutableLiveData(false)
@@ -58,149 +77,127 @@ class WithdrawManager(private val walletBackendApi:
WalletBackendApi) {
fun withdrawTestkudos() {
testWithdrawalInProgress.value = true
-
- walletBackendApi.sendRequest("withdrawTestkudos", null) { _, _ ->
+ walletBackendApi.sendRequest("withdrawTestkudos") { _, _ ->
testWithdrawalInProgress.postValue(false)
}
}
- fun getWithdrawalDetails(exchangeItem: ExchangeItem, amount: Amount) {
+ fun getWithdrawalDetails(uri: String) {
+ withdrawStatus.value = WithdrawStatus.Loading(uri)
val args = JSONObject().apply {
- put("exchangeBaseUrl", exchangeItem.exchangeBaseUrl)
- put("amount", amount.toJSONString())
+ put("talerWithdrawUri", uri)
}
- walletBackendApi.sendRequest("getWithdrawalDetailsForAmount", args) {
isError, result ->
- //
{"rawAmount":"TESTKUDOS:5","effectiveAmount":"TESTKUDOS:4.8","paytoUris":["payto:\/\/x-taler-bank\/bank.test.taler.net\/Exchange"],"tosAccepted":false}
+ walletBackendApi.sendRequest("getWithdrawalDetailsForUri", args) {
isError, result ->
if (isError) {
- Log.e(TAG, "$result")
+ handleError("getWithdrawalDetailsForUri", result)
+ return@sendRequest
+ }
+ val details: WithdrawalDetailsForUri =
mapper.readValue(result.toString())
+ if (details.defaultExchangeBaseUrl == null) {
+ // TODO go to exchange selection screen instead
+ val chosenExchange =
details.possibleExchanges[0].exchangeBaseUrl
+ getWithdrawalDetails(chosenExchange, details.amount, uri)
} else {
- Log.e(TAG, "$result")
+ getWithdrawalDetails(details.defaultExchangeBaseUrl,
details.amount, uri)
}
}
}
- fun getWithdrawalInfo(talerWithdrawUri: String) {
+ fun getWithdrawalDetails(exchangeBaseUrl: String, amount: Amount, uri:
String? = null) {
+ withdrawStatus.value = WithdrawStatus.Loading(uri)
val args = JSONObject().apply {
- put("talerWithdrawUri", talerWithdrawUri)
+ put("exchangeBaseUrl", exchangeBaseUrl)
+ put("amount", amount.toJSONString())
}
- withdrawStatus.value = WithdrawStatus.Loading(talerWithdrawUri)
-
- walletBackendApi.sendRequest("getWithdrawDetailsForUri", args) {
isError, result ->
+ walletBackendApi.sendRequest("getWithdrawalDetailsForAmount", args) {
isError, result ->
if (isError) {
- Log.e(TAG, "Error getWithdrawDetailsForUri
${result.toString(4)}")
- val message = if (result.has("message"))
result.getString("message") else null
- withdrawStatus.postValue(WithdrawStatus.Error(message))
+ handleError("getWithdrawalDetailsForAmount", result)
return@sendRequest
}
- Log.v(TAG, "got getWithdrawDetailsForUri result")
- val status = withdrawStatus.value
- if (status !is WithdrawStatus.Loading) {
- Log.v(TAG, "ignoring withdrawal info result, not loading.")
- return@sendRequest
- }
- val wi = result.getJSONObject("bankWithdrawDetails")
- val suggestedExchange = wi.getString("suggestedExchange")
- // We just use the suggested exchange, in the future there will be
- // a selection dialog.
- getWithdrawalInfoWithExchange(talerWithdrawUri, suggestedExchange)
+ val details: WithdrawalDetails =
mapper.readValue(result.toString())
+ if (details.tosAccepted)
+ withdrawStatus.value = ReceivedDetails(
+ talerWithdrawUri = uri,
+ exchangeBaseUrl = exchangeBaseUrl,
+ amountRaw = details.amountRaw,
+ amountEffective = details.amountEffective
+ )
+ else getExchangeTos(exchangeBaseUrl, details, uri)
}
}
- private fun getWithdrawalInfoWithExchange(talerWithdrawUri: String,
selectedExchange: String) {
+ private fun getExchangeTos(exchangeBaseUrl: String, details:
WithdrawalDetails, uri: String?) {
val args = JSONObject().apply {
- put("talerWithdrawUri", talerWithdrawUri)
- put("selectedExchange", selectedExchange)
+ put("exchangeBaseUrl", exchangeBaseUrl)
}
-
- walletBackendApi.sendRequest("getWithdrawDetailsForUri", args) {
isError, result ->
+ walletBackendApi.sendRequest("getExchangeTos", args) { isError, result
->
if (isError) {
- Log.e(TAG, "Error getWithdrawDetailsForUri
${result.toString(4)}")
- val message = if (result.has("message"))
result.getString("message") else null
- withdrawStatus.postValue(WithdrawStatus.Error(message))
- return@sendRequest
- }
- Log.v(TAG, "got getWithdrawDetailsForUri result (with exchange
details)")
- val status = withdrawStatus.value
- if (status !is WithdrawStatus.Loading) {
- Log.w(TAG, "ignoring withdrawal info result, not loading.")
+ handleError("getExchangeTos", result)
return@sendRequest
}
- val wi = result.getJSONObject("bankWithdrawDetails")
- val amount = Amount.fromJsonObject(wi.getJSONObject("amount"))
-
- val ei = result.getJSONObject("exchangeWithdrawDetails")
- val termsOfServiceAccepted =
ei.getBoolean("termsOfServiceAccepted")
-
- exchangeFees = ExchangeFees.fromExchangeWithdrawDetailsJson(ei)
-
- val withdrawFee =
Amount.fromJsonObject(ei.getJSONObject("withdrawFee"))
- val overhead = Amount.fromJsonObject(ei.getJSONObject("overhead"))
- val fee = withdrawFee + overhead
-
- if (!termsOfServiceAccepted) {
- val exchange = ei.getJSONObject("exchangeInfo")
- val tosText = exchange.getString("termsOfServiceText")
- val tosEtag = exchange.optString("termsOfServiceLastEtag",
"undefined")
- withdrawStatus.postValue(
- WithdrawStatus.TermsOfServiceReviewRequired(
- status.talerWithdrawUri,
- selectedExchange, tosText, tosEtag,
- amount, fee
- )
- )
- } else {
- withdrawStatus.postValue(
- ReceivedDetails(
- status.talerWithdrawUri,
- selectedExchange, amount,
- fee
- )
- )
- }
+ withdrawStatus.value = WithdrawStatus.TosReviewRequired(
+ talerWithdrawUri = uri,
+ exchangeBaseUrl = exchangeBaseUrl,
+ amountRaw = details.amountRaw,
+ amountEffective = details.amountEffective,
+ tosText = result.getString("tos"),
+ tosEtag = result.getString("currentEtag")
+ )
}
}
- fun acceptWithdrawal(talerWithdrawUri: String, selectedExchange: String,
currency: String) {
- val args = JSONObject()
- args.put("talerWithdrawUri", talerWithdrawUri)
- args.put("selectedExchange", selectedExchange)
-
- withdrawStatus.value = WithdrawStatus.Withdrawing(talerWithdrawUri)
-
- walletBackendApi.sendRequest("acceptWithdrawal", args) { isError,
result ->
+ /**
+ * Accept the currently displayed terms of service.
+ */
+ fun acceptCurrentTermsOfService() {
+ val s = withdrawStatus.value as WithdrawStatus.TosReviewRequired
+ val args = JSONObject().apply {
+ put("exchangeBaseUrl", s.exchangeBaseUrl)
+ put("etag", s.tosEtag)
+ }
+ walletBackendApi.sendRequest("setExchangeTosAccepted", args) {
isError, result ->
if (isError) {
- Log.v(TAG, "got acceptWithdrawal error result:
${result.toString(2)}")
- return@sendRequest
- }
- Log.v(TAG, "got acceptWithdrawal result")
- val status = withdrawStatus.value
- if (status !is WithdrawStatus.Withdrawing) {
- Log.w(TAG, "ignoring acceptWithdrawal result, invalid state:
$status")
+ handleError("setExchangeTosAccepted", result)
return@sendRequest
}
- withdrawStatus.postValue(WithdrawStatus.Success(currency))
+ withdrawStatus.value = ReceivedDetails(
+ talerWithdrawUri = s.talerWithdrawUri,
+ exchangeBaseUrl = s.exchangeBaseUrl,
+ amountRaw = s.amountRaw,
+ amountEffective = s.amountEffective
+ )
}
}
- /**
- * Accept the currently displayed terms of service.
- */
- fun acceptCurrentTermsOfService() {
- val s = withdrawStatus.value
- check(s is WithdrawStatus.TermsOfServiceReviewRequired)
+ @UiThread
+ fun acceptWithdrawal() {
+ val status = withdrawStatus.value as ReceivedDetails
+ val operation = if (status.talerWithdrawUri == null)
+ "acceptManualWithdrawal" else "acceptBankIntegratedWithdrawal"
val args = JSONObject().apply {
- put("exchangeBaseUrl", s.exchange)
- put("etag", s.tosEtag)
+ put("exchangeBaseUrl", status.exchangeBaseUrl)
+ if (status.talerWithdrawUri == null) {
+ put("amount", status.amountRaw)
+ } else {
+ put("talerWithdrawUri", status.talerWithdrawUri)
+ }
}
- walletBackendApi.sendRequest("acceptExchangeTermsOfService", args) {
isError, result ->
+ withdrawStatus.value = WithdrawStatus.Withdrawing
+ walletBackendApi.sendRequest(operation, args) { isError, result ->
if (isError) {
- Log.e(TAG, "Error acceptExchangeTermsOfService
${result.toString(4)}")
+ handleError(operation, result)
return@sendRequest
}
- val status = ReceivedDetails(s.talerWithdrawUri, s.exchange,
s.amount, s.fee)
- withdrawStatus.postValue(status)
+ withdrawStatus.value =
WithdrawStatus.Success(status.amountRaw.currency)
}
}
+ @UiThread
+ private fun handleError(operation: String, result: JSONObject) {
+ Log.e(TAG, "Error $operation ${result.toString(2)}")
+ val message = if (result.has("message")) result.getString("message")
else null
+ withdrawStatus.value = WithdrawStatus.Error(message)
+ }
+
}
diff --git a/wallet/src/main/res/drawable/ic_directions.xml
b/wallet/src/main/res/drawable/ic_directions.xml
deleted file mode 100644
index 229d69c..0000000
--- a/wallet/src/main/res/drawable/ic_directions.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- ~ 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/>
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M21.71,11.29l-9,-9c-0.39,-0.39 -1.02,-0.39
-1.41,0l-9,9c-0.39,0.39 -0.39,1.02 0,1.41l9,9c0.39,0.39 1.02,0.39
1.41,0l9,-9c0.39,-0.38 0.39,-1.01 0,-1.41zM14,14.5V12h-4v3H8v-4c0,-0.55 0.45,-1
1,-1h5V7.5l3.5,3.5 -3.5,3.5z" />
-</vector>
diff --git a/anastasis-ui/src/main/res/drawable/ic_baseline_check.xml
b/wallet/src/main/res/drawable/ic_edit.xml
similarity index 51%
copy from anastasis-ui/src/main/res/drawable/ic_baseline_check.xml
copy to wallet/src/main/res/drawable/ic_edit.xml
index 219e80e..69d2830 100644
--- a/anastasis-ui/src/main/res/drawable/ic_baseline_check.xml
+++ b/wallet/src/main/res/drawable/ic_edit.xml
@@ -5,6 +5,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
- android:fillColor="@android:color/white"
- android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"
/>
+ android:fillColor="#FFFFFF"
+
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39
0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83
3.75,3.75 1.83,-1.83z" />
</vector>
diff --git a/wallet/src/main/res/drawable/ic_history.xml
b/wallet/src/main/res/drawable/ic_history.xml
deleted file mode 100644
index d9f75ea..0000000
--- a/wallet/src/main/res/drawable/ic_history.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89
0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0
-3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03
9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z" />
-</vector>
diff --git a/wallet/src/main/res/drawable/transaction_payment_aborted.xml
b/wallet/src/main/res/drawable/transaction_payment_aborted.xml
deleted file mode 100644
index 8d47c26..0000000
--- a/wallet/src/main/res/drawable/transaction_payment_aborted.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- ~ 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/>
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:fillColor="#000"
- android:pathData="M15.46 18.12L16.88 19.54L19 17.41L21.12 19.54L22.54
18.12L20.41 16L22.54 13.88L21.12 12.46L19 14.59L16.88 12.46L15.46 13.88L17.59
16M14.97 11.62C14.86 10.28 13.58 8.97 12 9C10.3 9.04 9 10.3 9 12C9 13.7 10.3
14.94 12 15C12.39 15 12.77 14.92 13.14 14.77C13.41 13.67 13.86 12.63 14.97
11.62M13 16H7C7 14.9 6.1 14 5 14V10C6.1 10 7 9.1 7 8H17C17 9.1 17.9 10 19
10V10.05C19.67 10.06 20.34 10.18 21 10.4V6H3V18H13.32C13.1 17.33 13 16.66 13
16Z" />
-</vector>
diff --git a/wallet/src/main/res/drawable/transaction_tip_declined.xml
b/wallet/src/main/res/drawable/transaction_tip_declined.xml
deleted file mode 100644
index 4bd1633..0000000
--- a/wallet/src/main/res/drawable/transaction_tip_declined.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- ~ 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/>
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:fillColor="#000"
- android:pathData="M10 4A4 4 0 0 0 6 8A4 4 0 0 0 10 12A4 4 0 0 0 14 8A4
4 0 0 0 10 4M17.5 13C15 13 13 15 13 17.5C13 20 15 22 17.5 22C20 22 22 20 22
17.5C22 15 20 13 17.5 13M10 14C5.58 14 2 15.79 2 18V20H11.5A6.5 6.5 0 0 1 11
17.5A6.5 6.5 0 0 1 11.95 14.14C11.32 14.06 10.68 14 10 14M17.5 14.5C19.16 14.5
20.5 15.84 20.5 17.5C20.5 18.06 20.35 18.58 20.08 19L16 14.92C16.42 14.65 16.94
14.5 17.5 14.5M14.92 16L19 20.08C18.58 20.35 18.06 20.5 17.5 20.5C15.84 20.5
14.5 19.16 14.5 17.5C14. [...]
-</vector>
diff --git a/wallet/src/main/res/layout/fragment_json.xml
b/wallet/src/main/res/layout/fragment_json.xml
deleted file mode 100644
index d9bca8f..0000000
--- a/wallet/src/main/res/layout/fragment_json.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ 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/>
- -->
-
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/jsonView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginTop="8dp"
- android:layout_marginEnd="8dp"
- android:layout_marginBottom="8dp"
- android:fontFamily="monospace"
- android:textSize="12sp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- tools:text="[JSON]" />
-
-</ScrollView>
diff --git a/wallet/src/main/res/layout/fragment_prompt_withdraw.xml
b/wallet/src/main/res/layout/fragment_prompt_withdraw.xml
index c9c9402..6bca4ef 100644
--- a/wallet/src/main/res/layout/fragment_prompt_withdraw.xml
+++ b/wallet/src/main/res/layout/fragment_prompt_withdraw.xml
@@ -162,12 +162,12 @@
<ImageButton
android:id="@+id/selectExchangeButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
android:layout_marginEnd="16dp"
android:backgroundTint="@color/colorPrimary"
android:contentDescription="@string/nav_exchange_fees"
- android:src="@drawable/ic_cash_usd_outline"
+ android:src="@drawable/ic_edit"
android:tint="?attr/colorOnPrimary"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="@+id/withdrawExchangeUrl"
diff --git a/wallet/src/main/res/layout/fragment_transactions.xml
b/wallet/src/main/res/layout/fragment_transactions.xml
index 547da24..aaf638c 100644
--- a/wallet/src/main/res/layout/fragment_transactions.xml
+++ b/wallet/src/main/res/layout/fragment_transactions.xml
@@ -27,7 +27,7 @@
android:scrollbars="vertical"
android:visibility="invisible"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
- tools:listitem="@layout/list_item_history"
+ tools:listitem="@layout/list_item_transaction"
tools:visibility="visible" />
<TextView
diff --git a/wallet/src/main/res/layout/list_item_history.xml
b/wallet/src/main/res/layout/list_item_history.xml
deleted file mode 100644
index bc94738..0000000
--- a/wallet/src/main/res/layout/list_item_history.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ 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/>
- -->
-
-<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/selectable_background"
- android:foreground="?attr/selectableItemBackground"
- android:paddingStart="16dp"
- android:paddingTop="8dp"
- android:paddingEnd="16dp"
- android:paddingBottom="8dp">
-
- <ImageView
- android:id="@+id/icon"
- android:layout_width="32dp"
- android:layout_height="32dp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:tint="?android:colorControlNormal"
- tools:ignore="ContentDescription"
- tools:src="@drawable/ic_cash_usd_outline" />
-
- <TextView
- android:id="@+id/title"
- style="@style/TransactionTitle"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="8dp"
- app:layout_constraintEnd_toStartOf="@+id/amount"
- app:layout_constraintStart_toEndOf="@+id/icon"
- app:layout_constraintTop_toTopOf="parent"
- tools:text="@string/payment_title" />
-
- <TextView
- android:id="@+id/amount"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="24sp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- tools:text="- 1337.23" />
-
- <TextView
- android:id="@+id/time"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_marginEnd="8dp"
- android:textSize="14sp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@+id/amount"
- app:layout_constraintStart_toStartOf="@+id/title"
- app:layout_constraintTop_toBottomOf="@+id/title"
- tools:text="23 min ago" />
-
-</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/wallet/src/main/res/layout/payment_bottom_bar.xml
b/wallet/src/main/res/layout/payment_bottom_bar.xml
index a19ac4e..dbc60ae 100644
--- a/wallet/src/main/res/layout/payment_bottom_bar.xml
+++ b/wallet/src/main/res/layout/payment_bottom_bar.xml
@@ -32,7 +32,6 @@
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
- android:layout_marginBottom="27dp"
android:text="@string/payment_label_amount_total"
android:visibility="invisible"
app:layout_constraintBottom_toTopOf="@+id/confirmButton"
diff --git a/wallet/src/main/res/menu/activity_main_drawer.xml
b/wallet/src/main/res/menu/activity_main_drawer.xml
index 62abc32..d1cc462 100644
--- a/wallet/src/main/res/menu/activity_main_drawer.xml
+++ b/wallet/src/main/res/menu/activity_main_drawer.xml
@@ -43,10 +43,6 @@
android:id="@+id/nav_pending_operations"
android:icon="@drawable/ic_sync"
android:title="@string/pending_operations_title" />
- <item
- android:id="@+id/nav_history"
- android:icon="@drawable/ic_history"
- android:title="@string/nav_history" />
</group>
</menu>
</item>
diff --git a/wallet/src/main/res/navigation/nav_graph.xml
b/wallet/src/main/res/navigation/nav_graph.xml
index 93db557..285fac9 100644
--- a/wallet/src/main/res/navigation/nav_graph.xml
+++ b/wallet/src/main/res/navigation/nav_graph.xml
@@ -133,7 +133,7 @@
</fragment>
<fragment
android:id="@+id/selectExchangeFragment"
- android:name="net.taler.wallet.withdraw.SelectExchangeFragment"
+ android:name="net.taler.wallet.exchanges.SelectExchangeFragment"
android:label="@string/nav_exchange_fees"
tools:layout="@layout/fragment_select_exchange" />
@@ -143,12 +143,6 @@
android:label="@string/pending_operations_title"
tools:layout="@layout/fragment_pending_operations" />
- <fragment
- android:id="@+id/nav_history"
- android:name="net.taler.wallet.history.DevHistoryFragment"
- android:label="@string/nav_history"
- tools:layout="@layout/fragment_transactions" />
-
<fragment
android:id="@+id/nav_uri_input"
android:name="net.taler.wallet.UriInputFragment"
@@ -173,10 +167,6 @@
android:id="@+id/action_global_pending_operations"
app:destination="@id/nav_pending_operations" />
- <action
- android:id="@+id/action_global_history"
- app:destination="@id/nav_history" />
-
<action
android:id="@+id/action_nav_transaction_detail"
app:destination="@id/nav_transactions_detail" />
diff --git a/wallet/src/main/res/values/strings.xml
b/wallet/src/main/res/values/strings.xml
index 421d4ab..1e629a6 100644
--- a/wallet/src/main/res/values/strings.xml
+++ b/wallet/src/main/res/values/strings.xml
@@ -40,7 +40,6 @@ GNU Taler is immune against many types of fraud, such as
phishing of credit card
<string name="nav_prompt_withdraw">Withdraw Digital Cash</string>
<string name="nav_exchange_tos">Exchange\'s Terms of Service</string>
<string name="nav_exchange_fees">Exchange Fees</string>
- <string name="nav_history">Event History</string>
<string name="nav_error">Error</string>
<string name="button_back">Go Back</string>
diff --git a/wallet/src/main/res/xml/settings_backup.xml
b/wallet/src/main/res/xml/settings_backup.xml
index 52b72ac..f8c5839 100644
--- a/wallet/src/main/res/xml/settings_backup.xml
+++ b/wallet/src/main/res/xml/settings_backup.xml
@@ -14,7 +14,7 @@
~ GNU Taler; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
-->
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+<PreferenceScreen
xmlns:app="http://schemas.android.com/apk/res-auto">
<SwitchPreferenceCompat
diff --git
a/wallet/src/test/java/net/taler/wallet/crypto/Base32CrockfordTest.kt
b/wallet/src/test/java/net/taler/wallet/crypto/Base32CrockfordTest.kt
deleted file mode 100644
index 30332fc..0000000
--- a/wallet/src/test/java/net/taler/wallet/crypto/Base32CrockfordTest.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.wallet.crypto
-
-import org.junit.Test
-
-class Base32CrockfordTest {
- @Test
- fun testBasic() {
- val inputStr = "Hello, World"
- val data = inputStr.toByteArray(Charsets.UTF_8)
- val enc = Base32Crockford.encode(data)
- println(enc)
- val dec = Base32Crockford.decode(enc)
- val recoveredInputStr = dec.toString(Charsets.UTF_8)
- println(recoveredInputStr)
-
- val foo =
-
Base32Crockford.decode("51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30H2365338E9G6RT4AH1N6H13EGHR70RK6H1S6X2M4CSP8CSK8E1G88VKJH25610KGCHR8RWM4DJ47123CH9K89334D1S8N24ACJ48CR3EH256MR3AH1R711KCE9N6S134GSN6RW46D1H6CV3CDHJ6D0KEDHR6D24CD248MWKADHJ6WT34D25712KCD2474V46EA18H2M4GHM6WTK2E216S14CD238GSK0G9G692KCDHM6RW34CT16MV3CG9P60S34C1G70SMCHHQ8CVKJG9K6CVK6GHK70R46HJ26CR4AE9M8523ADHS8RR3EE1R74S32CHP6N1K0GT38D1M6C1R84TM2E9N8MSK2C1J71248E9H6H1MCD9J70VK4GSG6124CCHR6RS4ADSH8N0M4H1G84R4CD1G
[...]
- println(foo.toString(Charsets.UTF_8))
- }
-}
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [taler-taler-android] branch master updated (8eb241c -> 3ceaeb3),
gnunet <=