commit 6e21df1b59a134409d43d251175792d06e0451c6
parent 91867d8fd389f075673abf4f631745e4d0be09de
Author: Iván Ávalos <avalos@disroot.org>
Date: Wed, 22 May 2024 14:07:22 -0600
[wallet] WIP: initial work towards protocol v15 config
bug 0008838
Diffstat:
5 files changed, 416 insertions(+), 150 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
@@ -55,10 +55,30 @@ 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 {
+ ui.oldConfigForm.visibility = VISIBLE
+ ui.newConfigForm.visibility = GONE
+ }
+
+ ui.newConfigButton.setOnClickListener {
+ ui.oldConfigForm.visibility = GONE
+ ui.newConfigForm.visibility = VISIBLE
+ }
+
+ /*
+ * 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 +86,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 +100,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 +150,35 @@ 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
+ when (val config = configManager.config) {
+ is Config.Old -> {
+ 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
+ }
+
+ is Config.New -> {
+ ui.merchantUrlView.editText!!.setText(
+ if (isInitialization && config.merchantUrl.isBlank()) MERCHANT_URL_DEMO
+ else config.merchantUrl
+ )
+ ui.tokenView.editText!!.setText(
+ if (isInitialization && config.accessToken.isBlank()) MERCHANT_ACCESS_TOKEN_DEMO
+ else config.accessToken
+ )
+ }
+ }
+
}
private fun checkForUrlCredentials() {
@@ -158,8 +222,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
@@ -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,14 +46,27 @@ import net.taler.merchantpos.R
private const val SETTINGS_NAME = "taler-merchant-terminal"
+private const val SETTINGS_CONFIG_VERSION = "configVersion"
+
private const val SETTINGS_CONFIG_URL = "configUrl"
private const val SETTINGS_USERNAME = "username"
private const val SETTINGS_PASSWORD = "password"
+private const val SETTINGS_MERCHANT_URL = "merchantUrl"
+private const val SETTINGS_ACCESS_TOKEN = "accessToken"
+
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 MERCHANT_URL_DEMO = "https://backend.demo.taler.net/instances/pos"
+internal const val MERCHANT_ACCESS_TOKEN_DEMO = "sandbox"
+
+internal const val CONFIG_VERSION_OLD = 0
+internal const val CONFIG_VERSION_NEW = 1
+
+internal const val CONFIG_ACCESS_TOKEN_DEMO = ""
+
private val VERSION = Version.parse(BuildConfig.BACKEND_API_VERSION)!!
private val TAG = ConfigManager::class.java.simpleName
@@ -74,14 +88,15 @@ 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 = Config.New(
+ merchantUrl = prefs.getString(SETTINGS_MERCHANT_URL, "")!!,
+ accessToken = prefs.getString(SETTINGS_ACCESS_TOKEN, CONFIG_ACCESS_TOKEN_DEMO)!!,
)
+
@Volatile
var merchantConfig: MerchantConfig? = null
private set
+
@Volatile
var currency: String? = null
private set
@@ -97,16 +112,37 @@ 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
// get config from merchant backend API
@@ -165,18 +201,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
@@ -26,13 +26,32 @@ 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
diff --git 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,261 @@
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">
+ 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: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>
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/oldConfigForm"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ tools:visibility="visible">
- <com.google.android.material.textfield.TextInputEditText
- android:layout_width="match_parent"
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/configUrlView"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:inputType="textUri" />
+ 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.TextInputLayout>
+ <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
- android:id="@+id/usernameView"
- android:layout_width="0dp"
- 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.TextInputLayout>
- <com.google.android.material.textfield.TextInputEditText
- android:layout_width="match_parent"
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/usernameView"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:inputType="text" />
+ 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" />
- <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.TextInputLayout>
- <com.google.android.material.textfield.TextInputEditText
- android:layout_width="match_parent"
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/passwordView"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:inputType="textWebPassword" />
+ 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.TextInputLayout>
+ <com.google.android.material.textfield.TextInputEditText
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textWebPassword" />
- <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>
+
+ <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/strings.xml b/merchant-terminal/src/main/res/values/strings.xml
@@ -21,9 +21,13 @@
<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_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>