Add change server ability and respond to design feedback.
- Changed style of input boxes - Updated button behavior to only enable when values have changed - Added simple loading screen - Added error handling messages when the change server fails - Switched button order and simplified button text - Added red validation messages below input - Respond to user input, as they type - Reformatted title area to match other screens - Adjusted layouts to be percentage based to work more consistently on smaller screens - Implemented logic for restoring the original server values
This commit is contained in:
parent
d38626c205
commit
5e2f79ba62
|
@ -17,7 +17,18 @@ object Const {
|
|||
object Pref {
|
||||
const val FIRST_USE_VIEW_TX = "const.pref.first_use_view_tx"
|
||||
const val FEEDBACK_ENABLED = "const.pref.feedback_enabled"
|
||||
const val SERVER_NAME = "const.pref.server_name"
|
||||
const val SERVER_HOST = "const.pref.server_host"
|
||||
const val SERVER_PORT = "const.pref.server_port"
|
||||
}
|
||||
|
||||
/**
|
||||
* Default values to use application-wide. Ideally, this set of values should remain very short.
|
||||
*/
|
||||
object Default {
|
||||
object Server {
|
||||
// If you've forked the ECC repo, change this to your hosted lightwalletd instance
|
||||
const val HOST = "lightwalletd.electriccoin.co"//"your.hosted.lightwalletd.org"
|
||||
const val PORT = 9067
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,10 @@ import android.app.Dialog
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import androidx.core.content.getSystemService
|
||||
import cash.z.ecc.android.sdk.exception.LightWalletException
|
||||
import cash.z.ecc.android.ui.MainActivity
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
|
||||
|
@ -95,3 +98,31 @@ fun Context.showCriticalProcessorError(error: Throwable?, onRetry: () -> Unit =
|
|||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
fun Context.showUpdateServerCriticalError(userFacingMessage: String, onConfirm: () -> Unit = {}): Dialog {
|
||||
return MaterialAlertDialogBuilder(this)
|
||||
.setTitle("Failed to Change Server")
|
||||
.setMessage(userFacingMessage)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton("Ok") { d, _ ->
|
||||
d.dismiss()
|
||||
onConfirm()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
fun Context.showUpdateServerDialog(positiveText: String = "Update", onCancel: () -> Unit = {}, onUpdate: () -> Unit = {}): Dialog {
|
||||
return MaterialAlertDialogBuilder(this)
|
||||
.setTitle("Modify Lightwalletd Server?")
|
||||
.setMessage("WARNING: Entering an invalid or untrusted server might result in misconfiguration or loss of funds!")
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(positiveText) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
onUpdate()
|
||||
}
|
||||
.setNegativeButton("Cancel") { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
onCancel
|
||||
}
|
||||
.show()
|
||||
}
|
|
@ -1,13 +1,21 @@
|
|||
package cash.z.ecc.android.ui.settings
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import cash.z.ecc.android.di.viewmodel.viewModel
|
||||
import android.widget.Toast
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.ZcashWalletApp
|
||||
import cash.z.ecc.android.databinding.FragmentSettingsBinding
|
||||
import cash.z.ecc.android.ext.onClickNavBack
|
||||
import cash.z.ecc.android.di.viewmodel.viewModel
|
||||
import cash.z.ecc.android.ext.*
|
||||
import cash.z.ecc.android.sdk.exception.LightWalletException
|
||||
import cash.z.ecc.android.sdk.ext.collectWith
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SettingsFragment : BaseFragment<FragmentSettingsBinding>() {
|
||||
|
||||
|
@ -16,61 +24,131 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding>() {
|
|||
override fun inflate(inflater: LayoutInflater): FragmentSettingsBinding =
|
||||
FragmentSettingsBinding.inflate(inflater)
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
//
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
getCurrentServer()
|
||||
binding.hitAreaClose.onClickNavBack()
|
||||
binding.buttonUpdate.setOnClickListener(View.OnClickListener {
|
||||
validateServerHost(view)
|
||||
})
|
||||
binding.buttonReset.setOnClickListener(View.OnClickListener {
|
||||
resetServer()
|
||||
showUpdateServerDialog(view)
|
||||
})
|
||||
mainActivity?.preventBackPress(this)
|
||||
viewModel.init()
|
||||
binding.apply {
|
||||
groupLoading.gone()
|
||||
hitAreaExit.onClickNavBack()
|
||||
buttonReset.setOnClickListener(::onResetClicked)
|
||||
buttonUpdate.setOnClickListener(::onUpdateClicked)
|
||||
buttonUpdate.isActivated = true
|
||||
buttonReset.isActivated = true
|
||||
inputHost.doAfterTextChanged {
|
||||
viewModel.pendingHost = it.toString()
|
||||
}
|
||||
inputPort.doAfterTextChanged {
|
||||
viewModel.pendingPortText = it.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCurrentServer() {
|
||||
binding.inputTextLightwalletdServer.setText(viewModel.getServerHost())
|
||||
binding.inputTextLightwalletdPort.setText(viewModel.getServerPort().toString())
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.uiModels.collectWith(resumedScope, ::onUiModelUpdated)
|
||||
}
|
||||
|
||||
private fun resetServer() {
|
||||
|
||||
//
|
||||
// Event handlers
|
||||
//
|
||||
|
||||
private fun onResetClicked(unused: View?) {
|
||||
mainActivity?.hideKeyboard()
|
||||
context?.showUpdateServerDialog("Restore Defaults") {
|
||||
resumedScope.launch {
|
||||
binding.groupLoading.visible()
|
||||
binding.loadingView.requestFocus()
|
||||
viewModel.resetServer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateServerHost(view: View) {
|
||||
var isError = false
|
||||
if (binding.inputTextLightwalletdServer.text.toString().contains("http")) {
|
||||
binding.lightwalletdServer.error = "Please remove http:// or https://"
|
||||
isError = true
|
||||
private fun onUpdateClicked(unused: View?) {
|
||||
mainActivity?.hideKeyboard()
|
||||
context?.showUpdateServerDialog {
|
||||
resumedScope.launch {
|
||||
binding.groupLoading.visible()
|
||||
binding.loadingView.requestFocus()
|
||||
viewModel.submit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onUiModelUpdated(uiModel: SettingsViewModel.UiModel) {
|
||||
twig("onUiModelUpdated:::::$uiModel")
|
||||
binding.apply {
|
||||
if (handleCompletion(uiModel)) return@onUiModelUpdated
|
||||
|
||||
// avoid moving the cursor on instances where the change originated from the UI
|
||||
if (inputHost.text.toString() != uiModel.host) inputHost.setText(uiModel.host)
|
||||
if (inputPort.text.toString() != uiModel.portText) inputPort.setText(uiModel.portText)
|
||||
|
||||
buttonReset.isEnabled = uiModel.submitEnabled
|
||||
buttonUpdate.isEnabled = uiModel.submitEnabled && !uiModel.hasError
|
||||
|
||||
uiModel.hostErrorMessage.let { it ->
|
||||
textInputLayoutHost.helperText = it
|
||||
?: R.string.settings_host_helper_text.toAppString()
|
||||
textInputLayoutHost.setHelperTextColor(it.toHelperTextColor())
|
||||
}
|
||||
uiModel.portErrorMessage.let { it ->
|
||||
textInputLayoutPort.helperText = it
|
||||
?: R.string.settings_port_helper_text.toAppString()
|
||||
textInputLayoutPort.setHelperTextColor(it.toHelperTextColor())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the exit conditions and return true if we're done here.
|
||||
*/
|
||||
private fun handleCompletion(uiModel: SettingsViewModel.UiModel): Boolean {
|
||||
return if (uiModel.changeError != null) {
|
||||
binding.groupLoading.gone()
|
||||
onCriticalError(uiModel.changeError)
|
||||
true
|
||||
} else {
|
||||
binding.lightwalletdServer.error = null
|
||||
if (uiModel.complete) {
|
||||
binding.groupLoading.gone()
|
||||
mainActivity?.safeNavigate(R.id.nav_home)
|
||||
Toast.makeText(ZcashWalletApp.instance, "Successfully changed server!", Toast.LENGTH_SHORT).show()
|
||||
true
|
||||
}
|
||||
if (Integer.valueOf(binding.inputTextLightwalletdPort.text.toString()) > 65535) {
|
||||
binding.lightwalletdPort.error = "Please enter port number below 65535"
|
||||
isError = true
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun onCriticalError(error: Throwable) {
|
||||
val details = if (error is LightWalletException.ChangeServerException.StatusException) {
|
||||
error.status.description
|
||||
} else {
|
||||
binding.lightwalletdPort.error = null
|
||||
error.javaClass.simpleName
|
||||
}
|
||||
if (!isError) {
|
||||
showUpdateServerDialog(view)
|
||||
val message = "An error occured while changing servers. Please verify the info" +
|
||||
" and try again.\n\nError: $details"
|
||||
twig(message)
|
||||
Toast.makeText(ZcashWalletApp.instance, "Failed to change server!", Toast.LENGTH_SHORT).show()
|
||||
context?.showUpdateServerCriticalError(message)
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Utilities
|
||||
//
|
||||
|
||||
private fun String?.toHelperTextColor(): ColorStateList {
|
||||
val color = if (this == null) {
|
||||
R.color.text_light_dimmed
|
||||
} else {
|
||||
R.color.zcashRed
|
||||
}
|
||||
return ColorStateList.valueOf(color.toAppColor())
|
||||
}
|
||||
}
|
||||
|
||||
private fun showUpdateServerDialog(view: View) {
|
||||
MaterialAlertDialogBuilder(view.context)
|
||||
.setTitle("Modify lightwalletd Server?")
|
||||
.setMessage("WARNING: Entering an invalid or compromised lighthttpd server might result in misconfiguration or loss of funds.")
|
||||
.setCancelable(false)
|
||||
.setPositiveButton("Update") { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
updateServer()
|
||||
}
|
||||
.setNegativeButton("Cancel") { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun updateServer() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,99 @@
|
|||
package cash.z.ecc.android.ui.settings
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import cash.z.ecc.android.di.module.InitializerModule
|
||||
import cash.z.ecc.android.ext.Const
|
||||
import cash.z.ecc.android.lockbox.LockBox
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.cancellable
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import kotlin.properties.Delegates.observable
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class SettingsViewModel @Inject constructor() : ViewModel() {
|
||||
|
||||
@Inject
|
||||
lateinit var synchronizer: Synchronizer
|
||||
|
||||
fun updateServer(host: String, port: Int) {
|
||||
// TODO: Update the SecurePrefs here
|
||||
@Inject
|
||||
@Named(Const.Name.APP_PREFS)
|
||||
lateinit var prefs: LockBox
|
||||
|
||||
lateinit var uiModels: MutableStateFlow<UiModel>
|
||||
|
||||
private lateinit var initialServer: UiModel
|
||||
|
||||
var pendingHost by observable("", ::onUpdateModel)
|
||||
var pendingPortText by observable("", ::onUpdateModel)
|
||||
|
||||
|
||||
private fun getHost(): String {
|
||||
return prefs[Const.Pref.SERVER_HOST] ?: Const.Default.Server.HOST
|
||||
}
|
||||
|
||||
fun getServerHost(): String {
|
||||
return InitializerModule.defaultHost
|
||||
private fun getPort(): Int {
|
||||
return prefs[Const.Pref.SERVER_PORT] ?: Const.Default.Server.PORT
|
||||
}
|
||||
|
||||
fun getServerPort(): Int {
|
||||
return InitializerModule.defaultPort
|
||||
fun init() {
|
||||
initialServer = UiModel(getHost(), getPort().toString())
|
||||
uiModels = MutableStateFlow(initialServer)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
twig("SettingsViewModel cleared!")
|
||||
suspend fun resetServer() {
|
||||
UiModel(
|
||||
Const.Default.Server.HOST,
|
||||
Const.Default.Server.PORT.toString()
|
||||
).let { default ->
|
||||
uiModels.value = default
|
||||
submit()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun submit() {
|
||||
var error: Throwable? = null
|
||||
val host = uiModels.value.host
|
||||
val port = uiModels.value.portInt
|
||||
synchronizer.changeServer(uiModels.value.host, uiModels.value.portInt) {
|
||||
error = it
|
||||
}
|
||||
if (error == null) {
|
||||
prefs[Const.Pref.SERVER_HOST] = host
|
||||
prefs[Const.Pref.SERVER_PORT] = port
|
||||
}
|
||||
uiModels.value = uiModels.value.copy(changeError = error, complete = true)
|
||||
}
|
||||
|
||||
private fun onUpdateModel(kProperty: KProperty<*>, old: String, new: String) {
|
||||
val pendingPort = pendingPortText.toIntOrNull() ?: -1
|
||||
uiModels.value = UiModel(
|
||||
pendingHost,
|
||||
pendingPortText,
|
||||
pendingHost != initialServer.host || pendingPortText != initialServer.portText,
|
||||
if (!pendingHost.isValidHost()) "Please enter a valid host name or IP" else null,
|
||||
if (pendingPort >= 65535) "Please enter a valid port number below 65535" else null
|
||||
).also {
|
||||
twig("updated model with $it")
|
||||
}
|
||||
}
|
||||
|
||||
data class UiModel(
|
||||
val host: String = "",
|
||||
val portText: String = "",
|
||||
val submitEnabled: Boolean = false,
|
||||
val hostErrorMessage: String? = null,
|
||||
val portErrorMessage: String? = null,
|
||||
val changeError: Throwable? = null,
|
||||
val complete: Boolean = false
|
||||
) {
|
||||
val portInt get() = portText.toIntOrNull() ?: -1
|
||||
val hasError get() = hostErrorMessage != null || portErrorMessage != null
|
||||
}
|
||||
|
||||
// we can beef this up later if we want to but this is enough for now
|
||||
private fun String.isValidHost(): Boolean {
|
||||
return !contains("://")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:state_enabled="false" android:color="@color/text_light_dimmed" />
|
||||
<item android:state_activated="false" android:color="@color/text_light_dimmed"/>
|
||||
|
||||
<item android:state_activated="true" android:color="@color/text_light" />
|
||||
</selector>
|
|
@ -1,13 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<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="match_parent"
|
||||
android:background="@drawable/background_home">
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline_hit_area_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.04" />
|
||||
|
||||
<View
|
||||
android:id="@+id/hit_area_exit"
|
||||
android:layout_width="68dp"
|
||||
android:layout_height="68dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:alpha="0.3"
|
||||
android:background="@android:color/transparent"
|
||||
android:elevation="6dp"
|
||||
app:layout_constraintStart_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/guideline_hit_area_top" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon_profile"
|
||||
android:id="@+id/icon_exit"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:elevation="6dp"
|
||||
|
@ -15,107 +34,128 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.912"
|
||||
app:layout_constraintHorizontal_bias="0.088"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.064"
|
||||
app:layout_constraintVertical_bias="0.065"
|
||||
app:layout_constraintWidth_percent="0.08"
|
||||
app:srcCompat="@drawable/ic_cancel" />
|
||||
|
||||
<View
|
||||
android:id="@+id/hit_area_close"
|
||||
android:layout_width="68dp"
|
||||
android:layout_height="68dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:elevation="6dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:id="@+id/text_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="92dp"
|
||||
style="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:autoSizeTextType="uniform"
|
||||
android:maxLines="1"
|
||||
android:text="@string/settings_change_lightwalletd_server"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:textColor="@color/text_light"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
app:layout_constraintBottom_toBottomOf="@id/icon_exit"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/hit_area_exit"
|
||||
app:layout_constraintTop_toTopOf="@id/icon_exit" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/lightwalletd_server"
|
||||
android:id="@+id/text_input_layout_host"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:hint="@string/settings_server_address"
|
||||
app:errorEnabled="true"
|
||||
app:helperText="@string/settings_host_helper_text"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView"
|
||||
tools:layout_editor_absoluteX="1dp"
|
||||
tools:layout_editor_absoluteY="133dp">
|
||||
app:layout_constraintTop_toBottomOf="@+id/text_title"
|
||||
app:layout_constraintWidth_percent="0.84">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/input_text_lightwalletd_server"
|
||||
android:id="@+id/input_host"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/settings_server_address" />
|
||||
android:background="@android:color/transparent"
|
||||
android:imeOptions="actionNext"
|
||||
android:maxLength="253"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/text_light"
|
||||
android:textColorHint="@color/text_light_dimmed" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/lightwalletd_port"
|
||||
android:id="@+id/text_input_layout_port"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
app:errorEnabled="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/lightwalletd_server"
|
||||
tools:layout_editor_absoluteX="1dp"
|
||||
tools:layout_editor_absoluteY="133dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/input_text_lightwalletd_port"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="18dp"
|
||||
android:hint="@string/settings_server_port"
|
||||
app:errorEnabled="true"
|
||||
app:helperText="@string/settings_port_helper_text"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/text_input_layout_host"
|
||||
app:layout_constraintWidth_percent="0.84">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/input_port"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:inputType="number"
|
||||
android:maxLength="5" />
|
||||
android:maxLength="5"
|
||||
android:textColor="@color/text_light"
|
||||
android:textColorHint="@color/text_light_dimmed" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_update"
|
||||
style="@style/Zcash.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/settings_update"
|
||||
app:layout_constraintEnd_toStartOf="@+id/button_reset"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/lightwalletd_port" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_reset"
|
||||
style="@style/Zcash.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:text="@string/settings_reset_to_default_host"
|
||||
android:textColor="@color/text_light"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/button_update"
|
||||
app:layout_constraintTop_toBottomOf="@+id/lightwalletd_port" />
|
||||
android:layout_marginEnd="16dp"
|
||||
style="@style/Zcash.Button.OutlinedButton"
|
||||
android:text="@string/settings_reset"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:textColor="@color/selector_secondary_button_activatable"
|
||||
app:layout_constraintEnd_toStartOf="@id/button_update"
|
||||
app:layout_constraintTop_toTopOf="@id/button_update"
|
||||
app:strokeColor="@color/selector_secondary_button_activatable" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_update"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
style="@style/Zcash.Button"
|
||||
android:backgroundTint="@color/selector_primary_button_activatable"
|
||||
android:text="@string/settings_update"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:layout_constraintEnd_toEndOf="@id/text_input_layout_host"
|
||||
app:layout_constraintTop_toBottomOf="@+id/text_input_layout_port" />
|
||||
|
||||
<View
|
||||
android:id="@+id/loading_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/zcashWhite_24"
|
||||
android:clickable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:elevation="8dp" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/loading_progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="12dp"
|
||||
app:layout_constraintTop_toTopOf="@id/button_reset"
|
||||
app:layout_constraintBottom_toBottomOf="@id/button_reset"
|
||||
app:layout_constraintStart_toStartOf="@id/icon_exit"/>
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/group_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:constraint_referenced_ids="loading_progress,loading_view" />
|
||||
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -49,11 +49,13 @@
|
|||
<string name="profile_app_version">v1.0.0-alpha05</string>
|
||||
|
||||
<!-- Settings-->
|
||||
<string name="settings_change_lightwalletd_server">Change lightwalletd server:</string>
|
||||
<string name="settings_server_address">Server Address</string>
|
||||
<string name="settings_server_port">Server Port</string>
|
||||
<string name="settings_change_lightwalletd_server">Change Lightwalletd Server</string>
|
||||
<string name="settings_server_address">Host</string>
|
||||
<string name="settings_server_port">Port</string>
|
||||
<string name="settings_update">Update</string>
|
||||
<string name="settings_reset_to_default_host">Reset to Default Host</string>
|
||||
<string name="settings_reset">Reset</string>
|
||||
<string name="settings_port_helper_text">Enter a valid port number</string>
|
||||
<string name="settings_host_helper_text">Enter a valid host name or IP address</string>
|
||||
|
||||
<!-- Dialogs -->
|
||||
<string name="dialog_not_again">Don\'t show me again</string>
|
||||
|
|
Loading…
Reference in New Issue