aboutsummaryrefslogtreecommitdiff
path: root/merchant-terminal/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'merchant-terminal/src/main')
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigFragment.kt135
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt102
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/config/PosConfig.kt35
-rw-r--r--merchant-terminal/src/main/res/layout/fragment_merchant_config.xml368
-rw-r--r--merchant-terminal/src/main/res/values-de/strings.xml4
-rw-r--r--merchant-terminal/src/main/res/values-es/strings.xml28
-rw-r--r--merchant-terminal/src/main/res/values-fr/strings.xml4
-rw-r--r--merchant-terminal/src/main/res/values-ru/strings.xml72
-rw-r--r--merchant-terminal/src/main/res/values/strings.xml5
9 files changed, 577 insertions, 176 deletions
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigFragment.kt
index 9a5b7a7..d2167db 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigFragment.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigFragment.kt
@@ -55,10 +55,28 @@ class ConfigFragment : Fragment() {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ ui.configToggle.check(when (configManager.config) {
+ is Config.Old -> R.id.oldConfigButton
+ is Config.New -> R.id.newConfigButton
+ })
+
+ ui.oldConfigButton.setOnClickListener {
+ showOldConfig()
+ }
+
+ ui.newConfigButton.setOnClickListener {
+ showNewConfig()
+ }
+
+ /*
+ * Old configuration (JSON)
+ */
+
ui.configUrlView.editText!!.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) checkForUrlCredentials()
}
- ui.okButton.setOnClickListener {
+
+ ui.okOldButton.setOnClickListener {
checkForUrlCredentials()
val inputUrl = ui.configUrlView.editText!!.text
val url = if (inputUrl.startsWith("http")) {
@@ -66,9 +84,9 @@ class ConfigFragment : Fragment() {
} else {
"https://$inputUrl".also { ui.configUrlView.editText!!.setText(it) }
}
- ui.progressBar.visibility = VISIBLE
- ui.okButton.visibility = INVISIBLE
- val config = Config(
+ ui.progressBarOld.visibility = VISIBLE
+ ui.okOldButton.visibility = INVISIBLE
+ val config = Config.Old(
configUrl = url,
username = ui.usernameView.editText!!.text.toString(),
password = ui.passwordView.editText!!.text.toString()
@@ -80,12 +98,41 @@ class ConfigFragment : Fragment() {
}
}
}
+
ui.forgetPasswordButton.setOnClickListener {
configManager.forgetPassword()
ui.passwordView.editText!!.text = null
ui.forgetPasswordButton.visibility = GONE
}
+
ui.configDocsView.movementMethod = LinkMovementMethod.getInstance()
+
+ /*
+ * New configuration (Merchant)
+ */
+
+ ui.okNewButton.setOnClickListener {
+ val inputUrl = ui.merchantUrlView.editText!!.text
+ val url = if (inputUrl.startsWith("http")) {
+ inputUrl.toString()
+ } else {
+ "https://$inputUrl".also { ui.merchantUrlView.editText!!.setText(it) }
+ }
+
+ ui.progressBarNew.visibility = VISIBLE
+ ui.okNewButton.visibility = INVISIBLE
+ val config = Config.New(
+ merchantUrl = url,
+ accessToken = ui.tokenView.editText!!.text.toString(),
+ )
+ configManager.fetchConfig(config, true, ui.saveTokenCheckBox.isChecked)
+ configManager.configUpdateResult.observe(viewLifecycleOwner) { result ->
+ if (onConfigUpdate(result)) {
+ configManager.configUpdateResult.removeObservers(viewLifecycleOwner)
+ }
+ }
+ }
+
updateView(savedInstanceState == null)
}
@@ -101,20 +148,66 @@ class ConfigFragment : Fragment() {
}
private fun updateView(isInitialization: Boolean = false) {
- val config = configManager.config
- ui.configUrlView.editText!!.setText(
- if (isInitialization && config.configUrl.isBlank()) CONFIG_URL_DEMO
- else config.configUrl
- )
- ui.usernameView.editText!!.setText(
- if (isInitialization && config.username.isBlank()) CONFIG_USERNAME_DEMO
- else config.username
- )
- ui.passwordView.editText!!.setText(
- if (isInitialization && config.password.isBlank()) CONFIG_PASSWORD_DEMO
- else config.password
- )
- ui.forgetPasswordButton.visibility = if (config.hasPassword()) VISIBLE else GONE
+ if (isInitialization) {
+ ui.configUrlView.editText!!.setText(OLD_CONFIG_URL_DEMO)
+ ui.usernameView.editText!!.setText(OLD_CONFIG_USERNAME_DEMO)
+ ui.passwordView.editText!!.setText(OLD_CONFIG_PASSWORD_DEMO)
+
+ ui.merchantUrlView.editText!!.setText(NEW_CONFIG_URL_DEMO)
+ ui.tokenView.editText!!.setText(NEW_CONFIG_ACCESS_TOKEN_DEMO)
+
+ when (val config = configManager.config) {
+ is Config.Old -> {
+ if (config.configUrl.isNotBlank()) {
+ ui.configUrlView.editText!!.setText(config.configUrl)
+ }
+
+ if (config.username.isNotBlank()) {
+ ui.usernameView.editText!!.setText(config.username)
+ }
+
+ if (config.password.isNotBlank()) {
+ ui.passwordView.editText!!.setText(config.password)
+ }
+ }
+
+ is Config.New -> {
+ if (config.merchantUrl.isNotBlank()) {
+ ui.merchantUrlView.editText!!.setText(config.merchantUrl)
+ }
+
+ if (config.accessToken.isNotBlank()) {
+ ui.tokenView.editText!!.setText(config.accessToken)
+ }
+ }
+ }
+ }
+
+ when (val config = configManager.config) {
+ is Config.Old -> {
+ ui.configToggle.check(R.id.oldConfigButton)
+ showOldConfig()
+
+ ui.forgetPasswordButton.visibility = if (config.hasPassword()) VISIBLE else GONE
+ }
+ is Config.New -> {
+ ui.configToggle.check(R.id.newConfigButton)
+ showNewConfig()
+
+ ui.tokenView.visibility = if (config.hasPassword()) VISIBLE else GONE
+ }
+ }
+
+ }
+
+ private fun showOldConfig() {
+ ui.oldConfigForm.visibility = VISIBLE
+ ui.newConfigForm.visibility = GONE
+ }
+
+ private fun showNewConfig() {
+ ui.oldConfigForm.visibility = GONE
+ ui.newConfigForm.visibility = VISIBLE
}
private fun checkForUrlCredentials() {
@@ -158,8 +251,10 @@ class ConfigFragment : Fragment() {
}
private fun onResultReceived() {
- ui.progressBar.visibility = INVISIBLE
- ui.okButton.visibility = VISIBLE
+ ui.progressBarOld.visibility = INVISIBLE
+ ui.okOldButton.visibility = VISIBLE
+ ui.progressBarNew.visibility = INVISIBLE
+ ui.okNewButton.visibility = VISIBLE
}
}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt
index 70c7b18..7531a4a 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt
@@ -18,6 +18,7 @@ package net.taler.merchantpos.config
import android.content.Context
import android.content.Context.MODE_PRIVATE
+import android.net.Uri
import android.util.Base64.NO_WRAP
import android.util.Base64.encodeToString
import android.util.Log
@@ -45,13 +46,28 @@ import net.taler.merchantpos.R
private const val SETTINGS_NAME = "taler-merchant-terminal"
+private const val SETTINGS_CONFIG_VERSION = "configVersion"
+
+internal const val CONFIG_VERSION_OLD = 0
+internal const val CONFIG_VERSION_NEW = 1
+
+// Old JSON config + basic auth config
+
private const val SETTINGS_CONFIG_URL = "configUrl"
private const val SETTINGS_USERNAME = "username"
private const val SETTINGS_PASSWORD = "password"
-internal const val CONFIG_URL_DEMO = "https://docs.taler.net/_static/sample-pos-config.json"
-internal const val CONFIG_USERNAME_DEMO = ""
-internal const val CONFIG_PASSWORD_DEMO = ""
+internal const val OLD_CONFIG_URL_DEMO = "https://docs.taler.net/_static/sample-pos-config.json"
+internal const val OLD_CONFIG_USERNAME_DEMO = ""
+internal const val OLD_CONFIG_PASSWORD_DEMO = ""
+
+// New merchant API + token config
+
+private const val SETTINGS_MERCHANT_URL = "merchantUrl"
+private const val SETTINGS_ACCESS_TOKEN = "accessToken"
+
+internal const val NEW_CONFIG_URL_DEMO = "https://backend.demo.taler.net/instances/pos"
+internal const val NEW_CONFIG_ACCESS_TOKEN_DEMO = "sandbox"
private val VERSION = Version.parse(BuildConfig.BACKEND_API_VERSION)!!
@@ -74,14 +90,23 @@ class ConfigManager(
private val prefs = context.getSharedPreferences(SETTINGS_NAME, MODE_PRIVATE)
private val configurationReceivers = ArrayList<ConfigurationReceiver>()
- var config = Config(
- configUrl = prefs.getString(SETTINGS_CONFIG_URL, "")!!,
- username = prefs.getString(SETTINGS_USERNAME, CONFIG_USERNAME_DEMO)!!,
- password = prefs.getString(SETTINGS_PASSWORD, CONFIG_PASSWORD_DEMO)!!
- )
+ var config: Config = if (prefs.getInt(SETTINGS_CONFIG_VERSION, CONFIG_VERSION_NEW) == CONFIG_VERSION_NEW) {
+ Config.New(
+ merchantUrl = prefs.getString(SETTINGS_MERCHANT_URL, "")!!,
+ accessToken = prefs.getString(SETTINGS_ACCESS_TOKEN, NEW_CONFIG_ACCESS_TOKEN_DEMO)!!,
+ )
+ } else {
+ Config.Old(
+ configUrl = prefs.getString(SETTINGS_CONFIG_URL, "")!!,
+ username = prefs.getString(SETTINGS_USERNAME, OLD_CONFIG_USERNAME_DEMO)!!,
+ password = prefs.getString(SETTINGS_PASSWORD, OLD_CONFIG_PASSWORD_DEMO)!!,
+ )
+ }
+
@Volatile
var merchantConfig: MerchantConfig? = null
private set
+
@Volatile
var currency: String? = null
private set
@@ -97,18 +122,44 @@ class ConfigManager(
fun fetchConfig(config: Config, save: Boolean, savePassword: Boolean = false) {
mConfigUpdateResult.value = null
val configToSave = if (save) {
- if (savePassword) config else config.copy(password = "")
+ if (savePassword) config else when (val c = config) {
+ is Config.Old -> c.copy(password = "")
+ is Config.New -> c.copy(accessToken = "")
+ }
} else null
scope.launch(Dispatchers.IO) {
try {
+ val url = when(val c = config) {
+ is Config.Old -> c.configUrl
+ is Config.New -> Uri.parse(c.merchantUrl)
+ .buildUpon()
+ .appendPath("private/pos")
+ .build()
+ .toString()
+ }
+
// get PoS configuration
- val posConfig: PosConfig = httpClient.get(config.configUrl) {
- val credentials = "${config.username}:${config.password}"
- val auth = ("Basic ${encodeToString(credentials.toByteArray(), NO_WRAP)}")
- header(Authorization, auth)
+ val posConfig: PosConfig = httpClient.get(url) {
+ when (val c = config) {
+ is Config.Old -> {
+ val credentials = "${c.username}:${c.password}"
+ val auth = ("Basic ${encodeToString(credentials.toByteArray(), NO_WRAP)}")
+ header(Authorization, auth)
+ }
+ is Config.New -> {
+ val token = "secret-token:${c.accessToken}"
+ val auth = ("Bearer $token")
+ header(Authorization, auth)
+ }
+ }
}.body()
- val merchantConfig = posConfig.merchantConfig
+
+ val merchantConfig = when (val c = config) {
+ is Config.Old -> posConfig.merchantConfig!!
+ is Config.New -> MerchantConfig(c.merchantUrl, "secret-token:${c.accessToken}")
+ }
+
// get config from merchant backend API
api.getConfig(merchantConfig.baseUrl).handleSuspend(::onNetworkError) {
onMerchantConfigReceived(configToSave, posConfig, merchantConfig, it)
@@ -165,18 +216,29 @@ class ConfigManager(
@UiThread
fun forgetPassword() {
- config = config.copy(password = "")
+ config = when (val c = config) {
+ is Config.Old -> c.copy(password = "")
+ is Config.New -> c.copy(accessToken = "")
+ }
saveConfig(config)
merchantConfig = null
}
@UiThread
private fun saveConfig(config: Config) {
- prefs.edit()
- .putString(SETTINGS_CONFIG_URL, config.configUrl)
- .putString(SETTINGS_USERNAME, config.username)
- .putString(SETTINGS_PASSWORD, config.password)
- .apply()
+ when (val c = config) {
+ is Config.Old -> prefs.edit()
+ .putInt(SETTINGS_CONFIG_VERSION, CONFIG_VERSION_OLD)
+ .putString(SETTINGS_CONFIG_URL, c.configUrl)
+ .putString(SETTINGS_USERNAME, c.username)
+ .putString(SETTINGS_PASSWORD, c.password)
+ .apply()
+ is Config.New -> prefs.edit()
+ .putInt(SETTINGS_CONFIG_VERSION, CONFIG_VERSION_NEW)
+ .putString(SETTINGS_MERCHANT_URL, c.merchantUrl)
+ .putString(SETTINGS_ACCESS_TOKEN, c.accessToken)
+ .apply()
+ }
}
private fun onNetworkError(msg: String) = scope.launch(Dispatchers.Main) {
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/PosConfig.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/PosConfig.kt
index 556f05f..634d3a3 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/config/PosConfig.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/config/PosConfig.kt
@@ -26,19 +26,38 @@ import net.taler.common.TalerUtils
import net.taler.merchantlib.MerchantConfig
import java.util.UUID
-data class Config(
- val configUrl: String,
- val username: String,
- val password: String
-) {
- fun isValid() = configUrl.isNotBlank()
- fun hasPassword() = password.isNotBlank()
+sealed class Config {
+ abstract fun isValid(): Boolean
+ abstract fun hasPassword(): Boolean
+
+ /**
+ * JSON config URL + user/password
+ */
+ data class Old(
+ val configUrl: String,
+ val username: String,
+ val password: String,
+ ): Config() {
+ override fun isValid() = configUrl.isNotBlank()
+ override fun hasPassword() = password.isNotBlank()
+ }
+
+ /**
+ * Merchant URL + access token
+ */
+ data class New(
+ val merchantUrl: String,
+ val accessToken: String,
+ ): Config() {
+ override fun isValid() = merchantUrl.isNotBlank()
+ override fun hasPassword() = accessToken.isNotBlank()
+ }
}
@Serializable
data class PosConfig(
@SerialName("config")
- val merchantConfig: MerchantConfig,
+ val merchantConfig: MerchantConfig? = null ,
val categories: List<Category>,
val products: List<ConfigProduct>
)
diff --git a/merchant-terminal/src/main/res/layout/fragment_merchant_config.xml b/merchant-terminal/src/main/res/layout/fragment_merchant_config.xml
index 0061a1c..f53ecf6 100644
--- a/merchant-terminal/src/main/res/layout/fragment_merchant_config.xml
+++ b/merchant-terminal/src/main/res/layout/fragment_merchant_config.xml
@@ -21,131 +21,279 @@
android:layout_height="match_parent"
android:fillViewport="true">
- <androidx.constraintlayout.widget.ConstraintLayout
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:orientation="vertical"
tools:context=".config.ConfigFragment">
- <com.google.android.material.textfield.TextInputLayout
- android:id="@+id/configUrlView"
- android:layout_width="0dp"
+ <com.google.android.material.button.MaterialButtonToggleGroup
+ android:id="@+id/configToggle"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:hint="@string/config_url"
- app:boxBackgroundColor="@android:color/transparent"
- app:boxBackgroundMode="outline"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent">
-
- <com.google.android.material.textfield.TextInputEditText
- android:layout_width="match_parent"
+ app:singleSelection="true"
+ app:checkedButton="@id/newConfigButton">
+ <Button
+ style="?attr/materialButtonOutlinedStyle"
+ android:id="@+id/oldConfigButton"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:inputType="textUri" />
-
- </com.google.android.material.textfield.TextInputLayout>
+ android:layout_weight="1"
+ android:text="@string/config_old_label"/>
+ <Button
+ style="?attr/materialButtonOutlinedStyle"
+ android:id="@+id/newConfigButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/config_new_label" />
+ </com.google.android.material.button.MaterialButtonToggleGroup>
- <com.google.android.material.textfield.TextInputLayout
- android:id="@+id/usernameView"
- android:layout_width="0dp"
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/oldConfigForm"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:hint="@string/config_username"
- app:boxBackgroundColor="@android:color/transparent"
- app:boxBackgroundMode="outline"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/configUrlView">
-
- <com.google.android.material.textfield.TextInputEditText
+ android:visibility="gone"
+ tools:visibility="visible">
+
+ <androidx.cardview.widget.CardView
+ android:id="@+id/deprecationCard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:inputType="text" />
+ app:contentPadding="10dp"
+ android:layout_margin="16dp"
+ app:cardBackgroundColor="@color/red"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="center"
+ android:textColor="@android:color/white"
+ android:text="@string/config_old_deprecation" />
+ </androidx.cardview.widget.CardView>
- </com.google.android.material.textfield.TextInputLayout>
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/configUrlView"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:hint="@string/config_url"
+ app:boxBackgroundColor="@android:color/transparent"
+ app:boxBackgroundMode="outline"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/deprecationCard">
- <com.google.android.material.textfield.TextInputLayout
- android:id="@+id/passwordView"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:hint="@string/config_password"
- app:boxBackgroundColor="@android:color/transparent"
- app:boxBackgroundMode="outline"
- app:endIconMode="password_toggle"
- app:layout_constraintEnd_toStartOf="@+id/forgetPasswordButton"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/usernameView">
-
- <com.google.android.material.textfield.TextInputEditText
- android:layout_width="match_parent"
+ <com.google.android.material.textfield.TextInputEditText
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textUri" />
+
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/usernameView"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:inputType="textWebPassword" />
+ android:layout_margin="16dp"
+ android:hint="@string/config_username"
+ app:boxBackgroundColor="@android:color/transparent"
+ app:boxBackgroundMode="outline"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/configUrlView">
- </com.google.android.material.textfield.TextInputLayout>
+ <com.google.android.material.textfield.TextInputEditText
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="text" />
- <Button
- android:id="@+id/forgetPasswordButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:text="@string/config_forget_password"
- android:visibility="gone"
- app:layout_constraintBottom_toBottomOf="@+id/passwordView"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="@+id/passwordView"
- tools:visibility="visible" />
-
- <CheckBox
- android:id="@+id/savePasswordCheckBox"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="16dp"
- android:checked="true"
- android:text="@string/config_save_password"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@+id/okButton"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/passwordView"
- app:layout_constraintVertical_bias="0.0" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/okButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:text="@string/config_ok"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toEndOf="@+id/savePasswordCheckBox"
- app:layout_constraintTop_toBottomOf="@+id/passwordView"
- app:layout_constraintVertical_bias="0.0" />
-
- <ProgressBar
- android:id="@+id/progressBar"
- style="?android:attr/progressBarStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="@+id/okButton"
- app:layout_constraintEnd_toEndOf="@+id/okButton"
- app:layout_constraintStart_toStartOf="@+id/okButton"
- app:layout_constraintTop_toTopOf="@+id/okButton"
- tools:visibility="visible" />
-
- <TextView
- android:id="@+id/configDocsView"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:text="@string/config_docs"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/okButton" />
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/passwordView"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:hint="@string/config_password"
+ app:boxBackgroundColor="@android:color/transparent"
+ app:boxBackgroundMode="outline"
+ app:endIconMode="password_toggle"
+ app:layout_constraintEnd_toStartOf="@+id/forgetPasswordButton"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/usernameView">
+
+ <com.google.android.material.textfield.TextInputEditText
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textWebPassword" />
+
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <Button
+ android:id="@+id/forgetPasswordButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:text="@string/config_forget_password"
+ android:visibility="gone"
+ app:layout_constraintBottom_toBottomOf="@+id/passwordView"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/passwordView"
+ tools:visibility="visible" />
+
+ <CheckBox
+ android:id="@+id/savePasswordCheckBox"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:checked="true"
+ android:text="@string/config_save_password"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@+id/okOldButton"
+ app:layout_constraintHorizontal_chainStyle="spread_inside"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/passwordView"
+ app:layout_constraintVertical_bias="0.0" />
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/okOldButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:text="@string/config_ok"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/savePasswordCheckBox"
+ app:layout_constraintTop_toBottomOf="@+id/passwordView"
+ app:layout_constraintVertical_bias="0.0" />
+
+ <ProgressBar
+ android:id="@+id/progressBarOld"
+ style="?android:attr/progressBarStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="@+id/okOldButton"
+ app:layout_constraintEnd_toEndOf="@+id/okOldButton"
+ app:layout_constraintStart_toStartOf="@+id/okOldButton"
+ app:layout_constraintTop_toTopOf="@+id/okOldButton"
+ tools:visibility="visible" />
+
+ <TextView
+ android:id="@+id/configDocsView"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:text="@string/config_docs"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/okOldButton" />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/newConfigForm"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/merchantUrlView"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:hint="@string/config_merchant_url"
+ app:boxBackgroundColor="@android:color/transparent"
+ app:boxBackgroundMode="outline"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+
+ <com.google.android.material.textfield.TextInputEditText
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textUri" />
+
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/tokenView"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:hint="@string/config_token"
+ app:boxBackgroundColor="@android:color/transparent"
+ app:boxBackgroundMode="outline"
+ app:endIconMode="password_toggle"
+ app:layout_constraintEnd_toStartOf="@+id/forgetTokenButton"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/merchantUrlView">
+
+ <com.google.android.material.textfield.TextInputEditText
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textWebPassword" />
+
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <Button
+ android:id="@+id/forgetTokenButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:text="@string/config_forget_password"
+ android:visibility="gone"
+ app:layout_constraintBottom_toBottomOf="@+id/tokenView"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/tokenView"
+ tools:visibility="visible" />
+
+ <CheckBox
+ android:id="@+id/saveTokenCheckBox"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:checked="true"
+ android:text="@string/config_save_password"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@+id/okNewButton"
+ app:layout_constraintHorizontal_chainStyle="spread_inside"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/tokenView"
+ app:layout_constraintVertical_bias="0.0" />
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/okNewButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:text="@string/config_ok"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/saveTokenCheckBox"
+ app:layout_constraintTop_toBottomOf="@+id/tokenView"
+ app:layout_constraintVertical_bias="0.0" />
+
+ <ProgressBar
+ android:id="@+id/progressBarNew"
+ style="?android:attr/progressBarStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="@+id/okNewButton"
+ app:layout_constraintEnd_toEndOf="@+id/okNewButton"
+ app:layout_constraintStart_toStartOf="@+id/okNewButton"
+ app:layout_constraintTop_toTopOf="@+id/okNewButton"
+ tools:visibility="visible" />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
- </androidx.constraintlayout.widget.ConstraintLayout>
+ </LinearLayout>
</ScrollView>
diff --git a/merchant-terminal/src/main/res/values-de/strings.xml b/merchant-terminal/src/main/res/values-de/strings.xml
index 5936a52..7cad87c 100644
--- a/merchant-terminal/src/main/res/values-de/strings.xml
+++ b/merchant-terminal/src/main/res/values-de/strings.xml
@@ -9,8 +9,8 @@
<string name="order_total">Summe: %s</string>
<string name="order_restart">Neustart</string>
<string name="order_undo">Rückgängig machen</string>
- <string name="order_previous">Zurück</string>
- <string name="order_next">Vorwärts</string>
+ <string name="order_previous">Vorherige Bestellung</string>
+ <string name="order_next">Nächste Bestellung</string>
<string name="order_complete">Abschließen</string>
<string name="config_url">Konfigurations-URL</string>
<string name="config_ok">Konfiguration abrufen</string>
diff --git a/merchant-terminal/src/main/res/values-es/strings.xml b/merchant-terminal/src/main/res/values-es/strings.xml
index 4beb9fe..5b4903c 100644
--- a/merchant-terminal/src/main/res/values-es/strings.xml
+++ b/merchant-terminal/src/main/res/values-es/strings.xml
@@ -8,9 +8,9 @@
<string name="order_total">Total: %s</string>
<string name="order_restart">Reiniciar</string>
<string name="order_undo">Deshacer</string>
- <string name="order_previous">Ant</string>
- <string name="order_next">Sig</string>
- <string name="order_complete">Completa</string>
+ <string name="order_previous">Anterior</string>
+ <string name="order_next">Siguiente</string>
+ <string name="order_complete">Completar</string>
<string name="config_label">Ajustes comerciante</string>
<string name="config_username">Nombre de usuario</string>
<string name="config_password">Contraseña</string>
@@ -19,12 +19,12 @@
<string name="config_error_category">Error: No se encontró ninguna categoría de producto válida</string>
<string name="config_error_malformed">Error: La configuración JSON está malformada</string>
<string name="config_error_product_category_id">Error: Producto %1$s referencia categoría desconocida ID %2$d</string>
- <string name="config_error_product_zero">Error: No se pudieron encontrar productos válidos</string>
+ <string name="config_error_product_zero">Error: No se encontraron productos válidos</string>
<string name="config_error_unknown">Error: Configuración inválida</string>
- <string name="config_fetching">Recogiendo configuración…</string>
+ <string name="config_fetching">Obteniendo configuración…</string>
<string name="config_save_password">Recordar contraseña</string>
<string name="config_forget_password">Olvidar</string>
- <string name="config_fetching_label">Recogiendo configuración</string>
+ <string name="config_fetching_label">Obteniendo configuración</string>
<string name="payment_intro_nfc">Por favor permite al cliente escanear el código QR o usar NFC para pagar.</string>
<string name="payment_intro">Por favor permite al cliente escanear el código QR para pagar.</string>
<string name="payment_cancel">Cancelar pago</string>
@@ -37,23 +37,23 @@
<string name="history_unpaid">Impagado</string>
<string name="history_refund">Reembolso</string>
<string name="refund_amount">Cantidad</string>
- <string name="refund_reason">Razón de reembolso</string>
- <string name="refund_abort">Abortar</string>
+ <string name="refund_reason">Razón del reembolso</string>
+ <string name="refund_abort">Cancelar</string>
<string name="refund_complete">Recibido</string>
<string name="refund_confirm">Aprobar reembolso</string>
<string name="refund_error_invalid_amount">Cantidad inválida</string>
- <string name="refund_error_zero">Necesita ser una cantidad positiva</string>
+ <string name="refund_error_zero">Debe ser una cantidad positiva</string>
<string name="refund_error_backend">Error procesando el reembolso</string>
- <string name="refund_error_deadline">La fecha límite del reembolso ha pasado</string>
+ <string name="refund_error_deadline">El plazo del reembolso ha vencido</string>
<string name="refund_error_already_refunded">Ya reembolsado</string>
- <string name="refund_intro">Por favor permite al cliente escanear código QR para ofrecer reembolso</string>
+ <string name="refund_intro">Por favor permite al cliente escanear código QR para ofrecer un reembolso</string>
<string name="refund_order_ref">Referencia de compra : %1$s
\n
\n%2$s</string>
<string name="error_payment">Error: Pago no recibido</string>
<string name="error_timeout">No se ha realizado ningún pago dentro del periodo de pago, ¡inténtalo de nuevo!</string>
<string name="error_cancelled">Pago cancelado</string>
- <string name="error_history">Error recogiendo el historial de pedidos</string>
+ <string name="error_history">Error obteniendo el historial de pedidos</string>
<string name="toast_back_to_exit">Haz clic en «atrás» de nuevo para salir</string>
<string name="app_name">GNU Taler Punto de Venta</string>
<string name="app_name_short">Terminal de comercio</string>
@@ -61,9 +61,9 @@
<string name="config_error_network">Error: No se pudo conectar al servidor de configuración</string>
<string name="config_error_currency">Error: Producto %1$s tiene una divisa %2$s, pero %3$s esperada</string>
<string name="config_changed">Cambiado al nuevo comercio usando %s</string>
- <string name="config_docs">Por favor refiérete &lt;a href=\"https://docs.taler.net/taler-merchant-pos-terminal.html#apis-and-data-formats\"&gt;a la documentación&lt;/a&gt; para la configuración del formato.<a href="https://docs.taler.net/taler-merchant-pos-terminal.html#apis-and-data-formats">the documentation</a> for the configuration format.</string>
+ <string name="config_docs">Por favor consulta &lt;href=\"https://docs.taler.net/taler-merchant-pos-terminal.html#apis-and-data-formats\"&gt; la documentación&lt;/a&gt; para la configuración del formato.&lt;a href=\"https://docs.taler.net/taler-merchant-pos-terminal.html#apis-and-data-formats\"&gt;</string>
<string name="refund_error_max_amount">Mayor que la cantidad del pedido de %s</string>
- <string name="refund_intro_nfc">Por favor permite al cliente escanear el código QR o usar NFC para ofrecer reembolso</string>
+ <string name="refund_intro_nfc">Por favor permite al cliente escanear el código QR o usar NFC para ofrecer un reembolso</string>
<string name="payment_claimed">A la espera de que el cliente confirme el pago…</string>
<string name="order_custom_product">Nombre de producto personalizado</string>
<string name="order_custom_product_default">Consejo</string>
diff --git a/merchant-terminal/src/main/res/values-fr/strings.xml b/merchant-terminal/src/main/res/values-fr/strings.xml
index 4d9636d..7c4bd29 100644
--- a/merchant-terminal/src/main/res/values-fr/strings.xml
+++ b/merchant-terminal/src/main/res/values-fr/strings.xml
@@ -17,9 +17,9 @@
<string name="order_label_title">Commande numéro %s</string>
<string name="order_restart">Redémarrer</string>
<string name="order_undo">Annuler</string>
- <string name="order_previous">Préc.</string>
+ <string name="order_previous">Précédent</string>
<string name="order_next">Suivant</string>
- <string name="order_complete">Finalisé</string>
+ <string name="order_complete">Compléter</string>
<string name="config_label">Paramètres marchand</string>
<string name="config_url">URL de configuration</string>
<string name="config_ok">Récupérer la configuration</string>
diff --git a/merchant-terminal/src/main/res/values-ru/strings.xml b/merchant-terminal/src/main/res/values-ru/strings.xml
new file mode 100644
index 0000000..4a92343
--- /dev/null
+++ b/merchant-terminal/src/main/res/values-ru/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="config_error_unknown">Ошибка: Неправильная конфигурация</string>
+ <string name="config_fetching">Получение конфигурации…</string>
+ <string name="config_forget_password">Не сохранять пароль</string>
+ <string name="payment_intro_nfc">Для совершения платежа покупателем, разрешите сканирование QR-кода или использование NFC.</string>
+ <string name="payment_intro">Пожалуйста позвольте клиенту отсканировать QR-код для платежа.</string>
+ <string name="payment_cancel">Отменить оплату</string>
+ <string name="payment_received">Платёж получен</string>
+ <string name="payment_back_button">Продолжить</string>
+ <string name="payment_process_label">Требуется оплата</string>
+ <string name="payment_canceled">Оплата отменена</string>
+ <string name="history_label">История платежей</string>
+ <string name="history_unpaid">Неоплачено</string>
+ <string name="history_refund">Возврат</string>
+ <string name="refund_reason">Причина возврата</string>
+ <string name="refund_abort">Отменить</string>
+ <string name="refund_complete">Получено</string>
+ <string name="refund_confirm">Подтвердить возврат</string>
+ <string name="app_name">GNU Taler Точка Продажи</string>
+ <string name="app_name_short">Терминал продавца</string>
+ <string name="project_name">GNU Taler</string>
+ <string name="menu_order">Заказы</string>
+ <string name="menu_history">История</string>
+ <string name="menu_settings">Настройки</string>
+ <string name="order_label_title">Заказ №%s</string>
+ <string name="order_total">Итого: %s</string>
+ <string name="order_restart">Перезапустить</string>
+ <string name="order_undo">Отменить</string>
+ <string name="order_previous">Предыдущая</string>
+ <string name="order_next">Следующая</string>
+ <string name="order_custom">Добавить свой продукт</string>
+ <string name="order_complete">Выполнено</string>
+ <string name="order_custom_product">Название своего продукта</string>
+ <string name="order_custom_product_default">Чаевые</string>
+ <string name="order_custom_add_button">Добавить</string>
+ <string name="config_label">Настройки Продавца</string>
+ <string name="config_url">URL конфигурации</string>
+ <string name="config_username">Имя пользователя</string>
+ <string name="config_password">Пароль</string>
+ <string name="config_ok">Получить конфигурацию</string>
+ <string name="config_auth_error">Ошибка: Неверное имя пользователя или пароль</string>
+ <string name="config_error_network">Ошибка: не удается подключиться к серверу конфигурации</string>
+ <string name="config_error_category">Ошибка: не удается найти категорию товара</string>
+ <string name="config_error_malformed">Ошибка: Битый JSON конфигурации</string>
+ <string name="config_error_currency">Ошибка: Продукт %1$s имеет валюту %2$s, но ожидалось %3$s</string>
+ <string name="config_error_product_category_id">Ошибка: Продукт %1$s ссылается на неизвестную категорию с ID %2$d</string>
+ <string name="config_error_product_zero">Ошибка: не удается найти товар</string>
+ <string name="config_save_password">Запомнить пароль</string>
+ <string name="config_changed">Изменено на нового продавца используя %s</string>
+ <string name="config_fetching_label">Получение конфигурации</string>
+ <string name="config_docs">Пожалуйста перейдите к <a href="https://docs.taler.net/taler-merchant-pos-terminal.html#apis-and-data-formats">документации</a> по формату конфигурации</string>
+ <string name="payment_claimed">Ожидание пока клиент подтвердит платёж…</string>
+ <string name="payment_order_id">Чек №%s</string>
+ <string name="refund_amount">Сумма</string>
+ <string name="refund_error_max_amount">Сумма возврата превосходит сумму заказа %s</string>
+ <string name="refund_error_invalid_amount">Неправильное количество</string>
+ <string name="refund_error_zero">Сумма должна быть положительным числом</string>
+ <string name="refund_error_backend">Ошибка при обработке возврата</string>
+ <string name="refund_error_deadline">Возврат просрочен</string>
+ <string name="refund_error_already_refunded">Уже возвращено</string>
+ <string name="refund_intro_nfc">Пожалуйста дайте клиенту отсканировать QR-код или используйте NFC чтобы предложить возврат</string>
+ <string name="refund_intro">Пожалуйста дайте клиенту отсканировать QR-код чтобы предложить возврат</string>
+ <string name="refund_order_ref">Номер вашего заказа: %1$s
+\n
+\n%2$s</string>
+ <string name="error_payment">Ошибка: Никакого платежа не получено</string>
+ <string name="error_timeout">Платеж не был сделан за срок оплаты, пожалуйста попробуйте снова!</string>
+ <string name="error_cancelled">Платеж отменен</string>
+ <string name="error_history">Ошибка получения истории платежей</string>
+ <string name="toast_back_to_exit">Нажмите «назад» ещё раз чтобы выйти</string>
+</resources> \ No newline at end of file
diff --git a/merchant-terminal/src/main/res/values/strings.xml b/merchant-terminal/src/main/res/values/strings.xml
index 3cd2043..ff2cd44 100644
--- a/merchant-terminal/src/main/res/values/strings.xml
+++ b/merchant-terminal/src/main/res/values/strings.xml
@@ -21,9 +21,14 @@
<string name="order_custom_add_button">Add</string>
<string name="config_label">Merchant settings</string>
+ <string name="config_old_label">JSON file (old)</string>
+ <string name="config_new_label">Merchant (new)</string>
+ <string name="config_old_deprecation">This configuration method is deprecated, please use the new merchant API configuration.</string>
<string name="config_url">Configuration URL</string>
+ <string name="config_merchant_url">Merchant URL</string>
<string name="config_username">Username</string>
<string name="config_password">Password</string>
+ <string name="config_token">Access token</string>
<string name="config_ok">Fetch configuration</string>
<string name="config_auth_error">Error: Invalid username or password</string>
<string name="config_error_network">Error: Could not connect to configuration server</string>