Added Tunnel settings

This commit is contained in:
Sylvain Berfini 2024-05-20 16:01:43 +02:00
parent 26e1332421
commit 76716503e9
6 changed files with 409 additions and 15 deletions

View file

@ -91,6 +91,15 @@ class SettingsFragment : GenericMainFragment() {
} }
} }
private val tunnelModeListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
viewModel.tunnelModeIndex.value = position
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -212,6 +221,20 @@ class SettingsFragment : GenericMainFragment() {
binding.userInterfaceSettings.colorSpinner.onItemSelectedListener = colorListener binding.userInterfaceSettings.colorSpinner.onItemSelectedListener = colorListener
} }
// Tunnel mode
val tunnelModeAdapter = ArrayAdapter(
requireContext(),
R.layout.drop_down_item,
viewModel.tunnelModeLabels
)
tunnelModeAdapter.setDropDownViewResource(R.layout.generic_dropdown_cell)
binding.tunnelSettings.tunnelModeSpinner.adapter = tunnelModeAdapter
binding.tunnelSettings.tunnelModeSpinner.onItemSelectedListener = tunnelModeListener
viewModel.tunnelModeIndex.observe(viewLifecycleOwner) { index ->
binding.tunnelSettings.tunnelModeSpinner.setSelection(index)
}
startPostponedEnterTransition() startPostponedEnterTransition()
} }
@ -221,4 +244,12 @@ class SettingsFragment : GenericMainFragment() {
viewModel.reloadLdapServers() viewModel.reloadLdapServers()
viewModel.reloadConfiguredCardDavServers() viewModel.reloadConfiguredCardDavServers()
} }
override fun onPause() {
if (viewModel.isTunnelAvailable.value == true) {
viewModel.saveTunnelConfig()
}
super.onPause()
}
} }

View file

@ -30,7 +30,9 @@ import org.linphone.core.AudioDevice
import org.linphone.core.Conference import org.linphone.core.Conference
import org.linphone.core.Core import org.linphone.core.Core
import org.linphone.core.CoreListenerStub import org.linphone.core.CoreListenerStub
import org.linphone.core.Factory
import org.linphone.core.FriendList import org.linphone.core.FriendList
import org.linphone.core.Tunnel
import org.linphone.core.VFS import org.linphone.core.VFS
import org.linphone.core.tools.Log import org.linphone.core.tools.Log
import org.linphone.ui.GenericViewModel import org.linphone.ui.GenericViewModel
@ -51,6 +53,8 @@ class SettingsViewModel @UiThread constructor() : GenericViewModel() {
val expandMeetings = MutableLiveData<Boolean>() val expandMeetings = MutableLiveData<Boolean>()
val expandNetwork = MutableLiveData<Boolean>() val expandNetwork = MutableLiveData<Boolean>()
val expandUserInterface = MutableLiveData<Boolean>() val expandUserInterface = MutableLiveData<Boolean>()
val expandTunnel = MutableLiveData<Boolean>()
val isTunnelAvailable = MutableLiveData<Boolean>()
val recreateActivityEvent: MutableLiveData<Event<Boolean>> by lazy { val recreateActivityEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>() MutableLiveData<Event<Boolean>>()
@ -148,6 +152,19 @@ class SettingsViewModel @UiThread constructor() : GenericViewModel() {
"purple" "purple"
) )
// Tunnel settings
val tunnelMainHost = MutableLiveData<String>()
val tunnelMainPort = MutableLiveData<String>()
val tunnelDualMode = MutableLiveData<Boolean>()
val tunnelDualHost = MutableLiveData<String>()
val tunnelDualPort = MutableLiveData<String>()
val tunnelModeIndex = MutableLiveData<Int>()
val tunnelModeLabels = arrayListOf(
AppUtils.getString(R.string.settings_tunnel_mode_disabled_label),
AppUtils.getString(R.string.settings_tunnel_mode_always_label),
AppUtils.getString(R.string.settings_tunnel_mode_auto_label)
)
// Advanced settings // Advanced settings
val keepAliveThirdPartyAccountsService = MutableLiveData<Boolean>() val keepAliveThirdPartyAccountsService = MutableLiveData<Boolean>()
@ -180,6 +197,7 @@ class SettingsViewModel @UiThread constructor() : GenericViewModel() {
coreContext.postOnCoreThread { core -> coreContext.postOnCoreThread { core ->
core.addListener(coreListener) core.addListener(coreListener)
isTunnelAvailable.postValue(core.tunnelAvailable())
showConversationsSettings.postValue(!corePreferences.disableChat) showConversationsSettings.postValue(!corePreferences.disableChat)
showMeetingsSettings.postValue(!corePreferences.disableMeetings) showMeetingsSettings.postValue(!corePreferences.disableMeetings)
ldapAvailable.postValue(core.ldapAvailable()) ldapAvailable.postValue(core.ldapAvailable())
@ -195,6 +213,7 @@ class SettingsViewModel @UiThread constructor() : GenericViewModel() {
expandMeetings.value = false expandMeetings.value = false
expandNetwork.value = false expandNetwork.value = false
expandUserInterface.value = false expandUserInterface.value = false
expandTunnel.value = false
expandAudioDevices.value = false expandAudioDevices.value = false
expandAudioCodecs.value = false expandAudioCodecs.value = false
expandVideoCodecs.value = false expandVideoCodecs.value = false
@ -227,6 +246,10 @@ class SettingsViewModel @UiThread constructor() : GenericViewModel() {
theme.postValue(corePreferences.darkMode) theme.postValue(corePreferences.darkMode)
color.postValue(corePreferences.themeMainColor) color.postValue(corePreferences.themeMainColor)
if (core.tunnelAvailable()) {
setupTunnel()
}
keepAliveThirdPartyAccountsService.postValue(corePreferences.keepServiceAlive) keepAliveThirdPartyAccountsService.postValue(corePreferences.keepServiceAlive)
remoteProvisioningUrl.postValue(core.provisioningUri) remoteProvisioningUrl.postValue(core.provisioningUri)
@ -465,6 +488,63 @@ class SettingsViewModel @UiThread constructor() : GenericViewModel() {
color.value = colorName color.value = colorName
} }
@UiThread
fun toggleTunnelExpand() {
expandTunnel.value = expandTunnel.value == false
}
@WorkerThread
private fun setupTunnel() {
// Tunnel mode values are 0, 1 and 2, we can use selected item position directly
val tunnelConfig = coreContext.core.tunnel
if (tunnelConfig != null) {
val mainTunnel = tunnelConfig.servers.firstOrNull()
if (mainTunnel != null) {
tunnelMainHost.postValue(mainTunnel.host)
tunnelMainPort.postValue(mainTunnel.port.toString())
if (tunnelConfig.isDualModeEnabled) {
tunnelDualHost.postValue(mainTunnel.host2)
tunnelDualPort.postValue(mainTunnel.port2.toString())
}
}
tunnelDualMode.postValue(tunnelConfig.isDualModeEnabled)
tunnelModeIndex.postValue(tunnelConfig.mode.ordinal)
} else {
Log.w("$TAG No tunnel config found!")
tunnelModeIndex.postValue(0)
}
}
@UiThread
fun toggleTunnelDualMode() {
tunnelDualMode.value = tunnelDualMode.value == false
}
@UiThread
fun saveTunnelConfig() {
coreContext.postOnCoreThread { core ->
if (core.tunnelAvailable()) {
val tunnel = core.tunnel
tunnel?.cleanServers()
val config = Factory.instance().createTunnelConfig()
config.host = tunnelMainHost.value.orEmpty()
config.port = tunnelMainPort.value?.toInt() ?: 0
tunnel?.isDualModeEnabled = tunnelDualMode.value == true
if (tunnelDualMode.value == true) {
config.host2 = tunnelDualHost.value.orEmpty()
config.port2 = tunnelDualPort.value?.toInt() ?: 0
}
tunnel?.mode = Tunnel.Mode.fromInt(tunnelModeIndex.value ?: 0)
tunnel?.addServer(config)
Log.i("$TAG Tunnel configuration added into Core")
}
}
}
@UiThread @UiThread
fun toggleKeepAliveThirdPartyAccountService() { fun toggleKeepAliveThirdPartyAccountService() {
val newValue = keepAliveThirdPartyAccountsService.value == false val newValue = keepAliveThirdPartyAccountsService.value == false

View file

@ -63,8 +63,8 @@
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
style="@style/section_header_style" style="@style/section_header_style"
android:onClick="@{() -> viewModel.toggleSecurityExpand()}"
android:id="@+id/security" android:id="@+id/security"
android:onClick="@{() -> viewModel.toggleSecurityExpand()}"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="5dp" android:padding="5dp"
@ -86,14 +86,14 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:visibility="@{viewModel.expandSecurity ? View.VISIBLE : View.GONE}" android:visibility="@{viewModel.expandSecurity ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toBottomOf="@id/security" app:layout_constraintTop_toBottomOf="@id/security"
bind:viewModel="@{viewModel}"/> bind:viewModel="@{viewModel}"/>
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
style="@style/section_header_style" style="@style/section_header_style"
android:onClick="@{() -> viewModel.toggleCallsExpand()}"
android:id="@+id/calls" android:id="@+id/calls"
android:onClick="@{() -> viewModel.toggleCallsExpand()}"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="5dp" android:padding="5dp"
@ -115,14 +115,14 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:visibility="@{viewModel.expandCalls ? View.VISIBLE : View.GONE}" android:visibility="@{viewModel.expandCalls ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toBottomOf="@id/calls" app:layout_constraintTop_toBottomOf="@id/calls"
bind:viewModel="@{viewModel}"/> bind:viewModel="@{viewModel}"/>
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
style="@style/section_header_style" style="@style/section_header_style"
android:onClick="@{() -> viewModel.toggleConversationsExpand()}"
android:id="@+id/chat" android:id="@+id/chat"
android:onClick="@{() -> viewModel.toggleConversationsExpand()}"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="5dp" android:padding="5dp"
@ -145,14 +145,14 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:visibility="@{viewModel.expandConversations &amp;&amp; viewModel.showConversationsSettings ? View.VISIBLE : View.GONE}" android:visibility="@{viewModel.expandConversations &amp;&amp; viewModel.showConversationsSettings ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toBottomOf="@id/chat" app:layout_constraintTop_toBottomOf="@id/chat"
bind:viewModel="@{viewModel}"/> bind:viewModel="@{viewModel}"/>
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
style="@style/section_header_style" style="@style/section_header_style"
android:onClick="@{() -> viewModel.toggleContactsExpand()}"
android:id="@+id/contacts" android:id="@+id/contacts"
android:onClick="@{() -> viewModel.toggleContactsExpand()}"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="5dp" android:padding="5dp"
@ -175,14 +175,14 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:visibility="@{viewModel.expandContacts &amp;&amp; viewModel.showContactsSettings ? View.VISIBLE : View.GONE}" android:visibility="@{viewModel.expandContacts &amp;&amp; viewModel.showContactsSettings ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toBottomOf="@id/contacts" app:layout_constraintTop_toBottomOf="@id/contacts"
bind:viewModel="@{viewModel}"/> bind:viewModel="@{viewModel}"/>
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
style="@style/section_header_style" style="@style/section_header_style"
android:onClick="@{() -> viewModel.toggleMeetingsExpand()}"
android:id="@+id/meetings" android:id="@+id/meetings"
android:onClick="@{() -> viewModel.toggleMeetingsExpand()}"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="5dp" android:padding="5dp"
@ -205,14 +205,14 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:visibility="@{viewModel.expandMeetings &amp;&amp; viewModel.showMeetingsSettings ? View.VISIBLE : View.GONE}" android:visibility="@{viewModel.expandMeetings &amp;&amp; viewModel.showMeetingsSettings ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toBottomOf="@id/meetings" app:layout_constraintTop_toBottomOf="@id/meetings"
bind:viewModel="@{viewModel}"/> bind:viewModel="@{viewModel}"/>
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
style="@style/section_header_style" style="@style/section_header_style"
android:onClick="@{() -> viewModel.toggleNetworkExpand()}"
android:id="@+id/network" android:id="@+id/network"
android:onClick="@{() -> viewModel.toggleNetworkExpand()}"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="5dp" android:padding="5dp"
@ -234,14 +234,14 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:visibility="@{viewModel.expandNetwork ? View.VISIBLE : View.GONE}" android:visibility="@{viewModel.expandNetwork ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toBottomOf="@id/network" app:layout_constraintTop_toBottomOf="@id/network"
bind:viewModel="@{viewModel}"/> bind:viewModel="@{viewModel}"/>
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
style="@style/section_header_style" style="@style/section_header_style"
android:onClick="@{() -> viewModel.toggleUserInterfaceExpand()}"
android:id="@+id/user_interface" android:id="@+id/user_interface"
android:onClick="@{() -> viewModel.toggleUserInterfaceExpand()}"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="5dp" android:padding="5dp"
@ -263,10 +263,40 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:visibility="@{viewModel.expandUserInterface ? View.VISIBLE : View.GONE}" android:visibility="@{viewModel.expandUserInterface ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toBottomOf="@id/user_interface" app:layout_constraintTop_toBottomOf="@id/user_interface"
bind:viewModel="@{viewModel}"/> bind:viewModel="@{viewModel}"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/section_header_style"
android:id="@+id/tunnel"
android:onClick="@{() -> viewModel.toggleTunnelExpand()}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="5dp"
android:layout_marginStart="26dp"
android:layout_marginEnd="26dp"
android:layout_marginTop="16dp"
android:text="@string/settings_tunnel_title"
android:visibility="@{viewModel.isTunnelAvailable ? View.VISIBLE : View.GONE}"
android:drawableEnd="@{viewModel.expandTunnel ? @drawable/caret_up : @drawable/caret_down, default=@drawable/caret_up}"
android:drawableTint="?attr/color_main2_600"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/user_interface_settings"/>
<include
android:id="@+id/tunnel_settings"
layout="@layout/settings_tunnel"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:visibility="@{viewModel.isTunnelAvailable &amp;&amp; viewModel.expandTunnel ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toBottomOf="@id/tunnel"
bind:viewModel="@{viewModel}"/>
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
style="@style/section_header_style" style="@style/section_header_style"
android:id="@+id/advanced_settings" android:id="@+id/advanced_settings"
@ -284,7 +314,7 @@
app:layout_constraintVertical_bias="0" app:layout_constraintVertical_bias="0"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/user_interface_settings" app:layout_constraintTop_toBottomOf="@id/tunnel_settings"
app:layout_constraintBottom_toBottomOf="parent"/> app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,233 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View" />
<variable
name="viewModel"
type="org.linphone.ui.main.settings.viewmodel.SettingsViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:background="@drawable/shape_squircle_white_background">
<androidx.appcompat.widget.AppCompatTextView
style="@style/header_style"
android:id="@+id/tunnel_host_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="20dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:text="@string/settings_tunnel_main_host_label"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.appcompat.widget.AppCompatEditText
style="@style/default_text_style"
android:id="@+id/tunnel_host"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginEnd="16dp"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@={viewModel.tunnelMainHost}"
android:textSize="14sp"
android:textColor="@color/gray_main2_600"
android:maxLines="1"
android:background="@drawable/edit_text_background"
android:inputType="text|textUri"
android:hint="@string/settings_tunnel_main_host_label"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintWidth_max="@dimen/text_input_max_width"
app:layout_constraintTop_toBottomOf="@id/tunnel_host_label"
app:layout_constraintStart_toStartOf="@id/tunnel_host_label"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/header_style"
android:id="@+id/tunnel_port_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:text="@string/settings_tunnel_main_port_label"
app:layout_constraintTop_toBottomOf="@id/tunnel_host"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.appcompat.widget.AppCompatEditText
style="@style/default_text_style"
android:id="@+id/tunnel_port"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginEnd="16dp"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@={viewModel.tunnelMainPort}"
android:textSize="14sp"
android:textColor="@color/gray_main2_600"
android:maxLines="1"
android:background="@drawable/edit_text_background"
android:inputType="number|numberSigned"
android:hint="@string/settings_tunnel_main_port_label"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintWidth_max="@dimen/text_input_max_width"
app:layout_constraintTop_toBottomOf="@id/tunnel_port_label"
app:layout_constraintStart_toStartOf="@id/tunnel_port_label"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/settings_title_style"
android:onClick="@{() -> viewModel.toggleTunnelDualMode()}"
android:id="@+id/tunnel_dual_mode_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="10dp"
android:text="@string/settings_tunnel_dual_mode_label"
android:maxLines="2"
android:ellipsize="end"
app:layout_constraintTop_toTopOf="@id/tunnel_dual_mode_switch"
app:layout_constraintBottom_toBottomOf="@id/tunnel_dual_mode_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/tunnel_dual_mode_switch"/>
<com.google.android.material.materialswitch.MaterialSwitch
style="@style/material_switch_style"
android:id="@+id/tunnel_dual_mode_switch"
android:onClick="@{() -> viewModel.toggleTunnelDualMode()}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:checked="@{viewModel.tunnelDualMode}"
app:layout_constraintTop_toBottomOf="@id/tunnel_port"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/header_style"
android:id="@+id/tunnel_dual_host_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:text="@string/settings_tunnel_second_host_label"
android:visibility="@{viewModel.tunnelDualMode ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toBottomOf="@id/tunnel_dual_mode_switch"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.appcompat.widget.AppCompatEditText
style="@style/default_text_style"
android:id="@+id/tunnel_dual_host"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginEnd="16dp"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@={viewModel.tunnelDualHost}"
android:textSize="14sp"
android:textColor="@color/gray_main2_600"
android:maxLines="1"
android:background="@drawable/edit_text_background"
android:inputType="text|textUri"
android:hint="@string/settings_tunnel_second_host_label"
android:visibility="@{viewModel.tunnelDualMode ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintWidth_max="@dimen/text_input_max_width"
app:layout_constraintTop_toBottomOf="@id/tunnel_dual_host_label"
app:layout_constraintStart_toStartOf="@id/tunnel_dual_host_label"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/header_style"
android:id="@+id/tunnel_dual_port_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:text="@string/settings_tunnel_second_port_label"
android:visibility="@{viewModel.tunnelDualMode ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toBottomOf="@id/tunnel_dual_host"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.appcompat.widget.AppCompatEditText
style="@style/default_text_style"
android:id="@+id/tunnel_dual_port"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginEnd="16dp"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@={viewModel.tunnelDualPort}"
android:textSize="14sp"
android:textColor="@color/gray_main2_600"
android:maxLines="1"
android:background="@drawable/edit_text_background"
android:inputType="number|numberSigned"
android:hint="@string/settings_tunnel_second_port_label"
android:visibility="@{viewModel.tunnelDualMode ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintWidth_max="@dimen/text_input_max_width"
app:layout_constraintTop_toBottomOf="@id/tunnel_dual_port_label"
app:layout_constraintStart_toStartOf="@id/tunnel_dual_port_label"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/header_style"
android:id="@+id/tunnel_mode_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:text="@string/settings_tunnel_mode_label"
app:layout_constraintTop_toBottomOf="@id/tunnel_dual_port"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.appcompat.widget.AppCompatSpinner
style="@style/default_text_style"
android:id="@+id/tunnel_mode_spinner"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginEnd="16dp"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:textSize="14sp"
android:textColor="@color/gray_main2_600"
android:gravity="center_vertical"
android:overlapAnchor="false"
android:dropDownVerticalOffset="2dp"
android:spinnerMode="dropdown"
android:popupBackground="@drawable/shape_squircle_white_background"
android:background="@drawable/edit_text_background"
app:layout_constraintTop_toBottomOf="@id/tunnel_mode_label"
app:layout_constraintStart_toStartOf="@id/tunnel_mode_label"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="@+id/tunnel_mode_caret"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:src="@drawable/caret_down"
android:contentDescription="@null"
app:layout_constraintTop_toTopOf="@id/tunnel_mode_spinner"
app:layout_constraintBottom_toBottomOf="@id/tunnel_mode_spinner"
app:layout_constraintEnd_toEndOf="@id/tunnel_mode_spinner"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -273,6 +273,16 @@
<string name="settings_user_interface_light_theme_label">Clair</string> <string name="settings_user_interface_light_theme_label">Clair</string>
<string name="settings_user_interface_auto_theme_label">Auto</string> <string name="settings_user_interface_auto_theme_label">Auto</string>
<string name="settings_user_interface_color_title">Couleur principale</string> <string name="settings_user_interface_color_title">Couleur principale</string>
<string name="settings_tunnel_title">Tunnel</string>
<string name="settings_tunnel_main_host_label">Hôte</string>
<string name="settings_tunnel_main_port_label">Port</string>
<string name="settings_tunnel_dual_mode_label">Utiliser deux serveurs</string>
<string name="settings_tunnel_second_host_label">Hôte second serveur</string>
<string name="settings_tunnel_second_port_label">Port second serveur</string>
<string name="settings_tunnel_mode_label">Mode</string>
<string name="settings_tunnel_mode_disabled_label">Désactivé</string>
<string name="settings_tunnel_mode_always_label">Toujours</string>
<string name="settings_tunnel_mode_auto_label">Auto</string>
<string name="settings_advanced_title">Paramètres avancés</string> <string name="settings_advanced_title">Paramètres avancés</string>
<string name="settings_advanced_keep_alive_service_title">Garder l\'app en vie via un Service</string> <string name="settings_advanced_keep_alive_service_title">Garder l\'app en vie via un Service</string>

View file

@ -308,6 +308,16 @@
<string name="settings_user_interface_light_theme_label">Light theme</string> <string name="settings_user_interface_light_theme_label">Light theme</string>
<string name="settings_user_interface_auto_theme_label">Auto</string> <string name="settings_user_interface_auto_theme_label">Auto</string>
<string name="settings_user_interface_color_title">Main color</string> <string name="settings_user_interface_color_title">Main color</string>
<string name="settings_tunnel_title">Tunnel</string>
<string name="settings_tunnel_main_host_label">Host</string>
<string name="settings_tunnel_main_port_label">Port</string>
<string name="settings_tunnel_dual_mode_label">Use two servers</string>
<string name="settings_tunnel_second_host_label">Second host</string>
<string name="settings_tunnel_second_port_label">Second port</string>
<string name="settings_tunnel_mode_label">Mode</string>
<string name="settings_tunnel_mode_disabled_label">Disabled</string>
<string name="settings_tunnel_mode_always_label">Always</string>
<string name="settings_tunnel_mode_auto_label">Auto</string>
<string name="settings_advanced_title">Advanced settings</string> <string name="settings_advanced_title">Advanced settings</string>
<string name="settings_advanced_keep_alive_service_title">Keep app alive using Service</string> <string name="settings_advanced_keep_alive_service_title">Keep app alive using Service</string>