summaryrefslogtreecommitdiffhomepage
path: root/android/src
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2020-07-28 11:14:24 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2020-07-28 11:14:24 -0300
commit027f0c14685ab944b5313e846ab9e132d5b192d2 (patch)
treecc85f4a6f229e35d29848364ae33248d18caad4a /android/src
parentee608498b17b789d18070e31328a5d50ed16eb53 (diff)
parent4428e1fe3226924e5b879ddcff90ef95e22714ca (diff)
downloadmullvadvpn-027f0c14685ab944b5313e846ab9e132d5b192d2.tar.xz
mullvadvpn-027f0c14685ab944b5313e846ab9e132d5b192d2.zip
Merge branch 'create-cell-widget'
Diffstat (limited to 'android/src')
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListItemHolder.kt2
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/AdvancedFragment.kt49
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/CellInput.kt60
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/PreferencesFragment.kt10
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/RemainingTimeLabel.kt47
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt77
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SplitTunnellingFragment.kt6
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountCell.kt76
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AppVersionCell.kt90
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Cell.kt134
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/CellSwitch.kt (renamed from android/src/main/kotlin/net/mullvad/mullvadvpn/ui/CellSwitch.kt)2
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/MtuCell.kt91
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NavigateCell.kt60
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/ToggleCell.kt45
-rw-r--r--android/src/main/res/layout/advanced.xml100
-rw-r--r--android/src/main/res/layout/app_list_item.xml8
-rw-r--r--android/src/main/res/layout/mtu_edit_text.xml13
-rw-r--r--android/src/main/res/layout/preferences.xml70
-rw-r--r--android/src/main/res/layout/settings.xml180
-rw-r--r--android/src/main/res/layout/split_tunnelling_header.xml28
-rw-r--r--android/src/main/res/values/attrs.xml4
-rw-r--r--android/src/main/res/values/dimensions.xml13
22 files changed, 629 insertions, 536 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListItemHolder.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListItemHolder.kt
index b2d726f25e..b623721000 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListItemHolder.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListItemHolder.kt
@@ -9,7 +9,7 @@ import android.widget.TextView
import kotlin.properties.Delegates.observable
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.service.SplitTunnelling
-import net.mullvad.mullvadvpn.ui.CellSwitch
+import net.mullvad.mullvadvpn.ui.widget.CellSwitch
import net.mullvad.mullvadvpn.util.JobTracker
class AppListItemHolder(
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/AdvancedFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/AdvancedFragment.kt
index ba9ca142d8..826ebfceea 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/AdvancedFragment.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/AdvancedFragment.kt
@@ -1,20 +1,16 @@
package net.mullvad.mullvadvpn.ui
import android.os.Bundle
-import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.TextView
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.model.Settings
-
-private const val MIN_MTU_VALUE = 1280
-private const val MAX_MTU_VALUE = 1420
+import net.mullvad.mullvadvpn.ui.widget.MtuCell
+import net.mullvad.mullvadvpn.ui.widget.NavigateCell
class AdvancedFragment : ServiceDependentFragment(OnNoService.GoBack) {
- private lateinit var wireguardMtuInput: CellInput
- private lateinit var wireguardKeysMenu: View
+ private lateinit var wireguardMtuInput: MtuCell
private lateinit var titleController: CollapsibleTitleController
override fun onSafelyCreateView(
@@ -28,27 +24,20 @@ class AdvancedFragment : ServiceDependentFragment(OnNoService.GoBack) {
parentActivity.onBackPressed()
}
- wireguardMtuInput =
- CellInput(view.findViewById(R.id.wireguard_mtu_input), MIN_MTU_VALUE, MAX_MTU_VALUE)
-
- wireguardMtuInput.onSubmit = { mtu ->
- jobTracker.newBackgroundJob("updateMtu") {
- daemon.setWireguardMtu(mtu)
+ wireguardMtuInput = view.findViewById<MtuCell>(R.id.wireguard_mtu).apply {
+ onSubmit = { mtu ->
+ jobTracker.newBackgroundJob("updateMtu") {
+ daemon.setWireguardMtu(mtu)
+ }
}
}
- view.findViewById<TextView>(R.id.wireguard_mtu_footer).apply {
- text = context.getString(R.string.wireguard_mtu_footer, MIN_MTU_VALUE, MAX_MTU_VALUE)
- }
-
- wireguardKeysMenu = view.findViewById<View>(R.id.wireguard_keys).apply {
- setOnClickListener {
- openSubFragment(WireguardKeyFragment())
- }
+ view.findViewById<NavigateCell>(R.id.wireguard_keys).apply {
+ targetFragment = WireguardKeyFragment::class
}
- view.findViewById<View>(R.id.split_tunnelling).setOnClickListener {
- openSubFragment(SplitTunnellingFragment())
+ view.findViewById<NavigateCell>(R.id.split_tunnelling).apply {
+ targetFragment = SplitTunnellingFragment::class
}
settingsListener.subscribe(this) { settings ->
@@ -72,18 +61,4 @@ class AdvancedFragment : ServiceDependentFragment(OnNoService.GoBack) {
titleController.onDestroy()
settingsListener.unsubscribe(this)
}
-
- private fun openSubFragment(fragment: Fragment) {
- fragmentManager?.beginTransaction()?.apply {
- setCustomAnimations(
- R.anim.fragment_enter_from_right,
- R.anim.fragment_half_exit_to_left,
- R.anim.fragment_half_enter_from_left,
- R.anim.fragment_exit_to_right
- )
- replace(R.id.main_fragment, fragment)
- addToBackStack(null)
- commit()
- }
- }
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/CellInput.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/CellInput.kt
deleted file mode 100644
index dd40712e24..0000000000
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/CellInput.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-package net.mullvad.mullvadvpn.ui
-
-import android.text.Editable
-import android.text.TextWatcher
-import android.widget.EditText
-import net.mullvad.mullvadvpn.R
-
-class CellInput(val input: EditText, val minValue: Int, val maxValue: Int) {
- private val context = input.context
-
- private val validInputColor = context.getColor(R.color.white)
- private val invalidInputColor = context.getColor(R.color.red)
-
- var value
- get() = input.text.toString().trim().toIntOrNull()
- set(value) {
- input.setText(value?.toString() ?: "")
- }
-
- var onSubmit: ((Int?) -> Unit)? = null
-
- var hasFocus = false
- private set(value) {
- if (field != value) {
- field = value
-
- if (value == false) {
- val inputValue = this@CellInput.value
-
- if (inputValue == null || (inputValue >= minValue && inputValue <= maxValue)) {
- onSubmit?.invoke(inputValue)
- }
- }
- }
- }
-
- init {
- input.apply {
- addTextChangedListener(InputWatcher())
-
- setOnFocusChangeListener { _, newHasFocus -> hasFocus = newHasFocus }
- }
- }
-
- inner class InputWatcher : TextWatcher {
- override fun beforeTextChanged(text: CharSequence, start: Int, count: Int, after: Int) {}
-
- override fun onTextChanged(text: CharSequence, start: Int, count: Int, after: Int) {}
-
- override fun afterTextChanged(text: Editable) {
- val value = text.toString().trim().toIntOrNull()
-
- if (value != null && value >= minValue && value <= maxValue) {
- input.setTextColor(validInputColor)
- } else {
- input.setTextColor(invalidInputColor)
- }
- }
- }
-}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/PreferencesFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/PreferencesFragment.kt
index f996994e03..d090017501 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/PreferencesFragment.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/PreferencesFragment.kt
@@ -6,10 +6,12 @@ import android.view.View
import android.view.ViewGroup
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.model.Settings
+import net.mullvad.mullvadvpn.ui.widget.CellSwitch
+import net.mullvad.mullvadvpn.ui.widget.ToggleCell
class PreferencesFragment : ServiceDependentFragment(OnNoService.GoBack) {
- private lateinit var allowLanToggle: CellSwitch
- private lateinit var autoConnectToggle: CellSwitch
+ private lateinit var allowLanToggle: ToggleCell
+ private lateinit var autoConnectToggle: ToggleCell
private lateinit var titleController: CollapsibleTitleController
override fun onSafelyCreateView(
@@ -23,7 +25,7 @@ class PreferencesFragment : ServiceDependentFragment(OnNoService.GoBack) {
parentActivity.onBackPressed()
}
- allowLanToggle = view.findViewById<CellSwitch>(R.id.allow_lan_toggle).apply {
+ allowLanToggle = view.findViewById<ToggleCell>(R.id.allow_lan).apply {
forcefullySetState(boolToSwitchState(settingsListener.settings.allowLan))
listener = { state ->
@@ -34,7 +36,7 @@ class PreferencesFragment : ServiceDependentFragment(OnNoService.GoBack) {
}
}
- autoConnectToggle = view.findViewById<CellSwitch>(R.id.auto_connect_toggle).apply {
+ autoConnectToggle = view.findViewById<ToggleCell>(R.id.auto_connect).apply {
forcefullySetState(boolToSwitchState(settingsListener.settings.autoConnect))
listener = { state ->
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/RemainingTimeLabel.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/RemainingTimeLabel.kt
deleted file mode 100644
index 0a21cd5394..0000000000
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/RemainingTimeLabel.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package net.mullvad.mullvadvpn.ui
-
-import android.content.Context
-import android.view.View
-import android.widget.TextView
-import net.mullvad.mullvadvpn.R
-import net.mullvad.mullvadvpn.util.TimeLeftFormatter
-import org.joda.time.DateTime
-import org.joda.time.Duration
-
-class RemainingTimeLabel(val context: Context, val view: View) {
- private val resources = context.resources
- private val formatter = TimeLeftFormatter(resources)
-
- private val expiredColor = context.getColor(R.color.red)
- private val normalColor = context.getColor(R.color.white60)
-
- private val label = view.findViewById<TextView>(R.id.remaining_time)
-
- var accountExpiry: DateTime? = null
- set(value) {
- field = value
- updateLabel()
- }
-
- private fun updateLabel() {
- val expiry = accountExpiry
-
- if (expiry != null) {
- val remainingTime = Duration(DateTime.now(), expiry)
-
- if (remainingTime.isShorterThan(Duration.ZERO)) {
- label.setText(R.string.out_of_time)
- label.setTextColor(expiredColor)
- } else {
- label.setText(formatter.format(expiry, remainingTime))
- label.setTextColor(normalColor)
- }
- } else {
- label.text = ""
- }
- }
-
- private fun getRemainingText(pluralId: Int, quantity: Int): String {
- return resources.getQuantityString(pluralId, quantity, quantity)
- }
-}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt
index edc8bf518a..f0f4483e16 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt
@@ -1,27 +1,23 @@
package net.mullvad.mullvadvpn.ui
-import android.content.Intent
-import android.net.Uri
import android.os.Bundle
-import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageButton
-import android.widget.TextView
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.dataproxy.AppVersionInfoCache
import net.mullvad.mullvadvpn.service.AccountCache
+import net.mullvad.mullvadvpn.ui.widget.AccountCell
+import net.mullvad.mullvadvpn.ui.widget.AppVersionCell
+import net.mullvad.mullvadvpn.ui.widget.NavigateCell
class SettingsFragment : ServiceAwareFragment() {
- private lateinit var accountMenu: View
- private lateinit var appVersionWarning: View
- private lateinit var appVersionLabel: TextView
- private lateinit var appVersionFooter: View
+ private lateinit var accountMenu: AccountCell
+ private lateinit var appVersionMenu: AppVersionCell
private lateinit var preferencesMenu: View
private lateinit var advancedMenu: View
- private lateinit var remainingTimeLabel: RemainingTimeLabel
private lateinit var titleController: CollapsibleTitleController
private var active = false
@@ -58,36 +54,24 @@ class SettingsFragment : ServiceAwareFragment() {
parentActivity.quit()
}
- accountMenu = view.findViewById<View>(R.id.account).apply {
- setOnClickListener {
- openSubFragment(AccountFragment())
- }
+ accountMenu = view.findViewById<AccountCell>(R.id.account).apply {
+ targetFragment = AccountFragment::class
}
- preferencesMenu = view.findViewById<View>(R.id.preferences).apply {
- setOnClickListener {
- openSubFragment(PreferencesFragment())
- }
+ preferencesMenu = view.findViewById<NavigateCell>(R.id.preferences).apply {
+ targetFragment = PreferencesFragment::class
}
- advancedMenu = view.findViewById<View>(R.id.advanced).apply {
- setOnClickListener {
- openSubFragment(AdvancedFragment())
- }
+ advancedMenu = view.findViewById<NavigateCell>(R.id.advanced).apply {
+ targetFragment = AdvancedFragment::class
}
- view.findViewById<View>(R.id.app_version).setOnClickListener {
- openLink(R.string.download_url)
+ view.findViewById<NavigateCell>(R.id.report_a_problem).apply {
+ targetFragment = ProblemReportFragment::class
}
- view.findViewById<View>(R.id.report_a_problem).setOnClickListener {
- openSubFragment(ProblemReportFragment())
- }
+ appVersionMenu = view.findViewById<AppVersionCell>(R.id.app_version)
- appVersionWarning = view.findViewById(R.id.app_version_warning)
- appVersionLabel = view.findViewById<TextView>(R.id.app_version_label)
- appVersionFooter = view.findViewById(R.id.app_version_footer)
- remainingTimeLabel = RemainingTimeLabel(parentActivity, view)
titleController = CollapsibleTitleController(view)
return view
@@ -127,7 +111,7 @@ class SettingsFragment : ServiceAwareFragment() {
onAccountExpiryChange.subscribe(this@SettingsFragment) { expiry ->
jobTracker.newUiJob("updateAccountInfo") {
- remainingTimeLabel.accountExpiry = expiry
+ accountMenu.accountExpiry = expiry
}
}
@@ -141,26 +125,6 @@ class SettingsFragment : ServiceAwareFragment() {
}
}
- private fun openSubFragment(fragment: Fragment) {
- fragmentManager?.beginTransaction()?.apply {
- setCustomAnimations(
- R.anim.fragment_enter_from_right,
- R.anim.fragment_half_exit_to_left,
- R.anim.fragment_half_enter_from_left,
- R.anim.fragment_exit_to_right
- )
- replace(R.id.main_fragment, fragment)
- addToBackStack(null)
- commit()
- }
- }
-
- private fun openLink(urlResourceId: Int) {
- val intent = Intent(Intent.ACTION_VIEW, Uri.parse(parentActivity.getString(urlResourceId)))
-
- startActivity(intent)
- }
-
private fun updateLoggedInStatus(loggedIn: Boolean) {
val visibility = if (loggedIn) {
View.VISIBLE
@@ -177,14 +141,7 @@ class SettingsFragment : ServiceAwareFragment() {
val isOutdated = versionInfoCache?.isOutdated ?: false
val isSupported = versionInfoCache?.isSupported ?: true
- appVersionLabel.setText(versionInfoCache?.version ?: "")
-
- if (!isOutdated && isSupported) {
- appVersionWarning.visibility = View.GONE
- appVersionFooter.visibility = View.GONE
- } else {
- appVersionWarning.visibility = View.VISIBLE
- appVersionFooter.visibility = View.VISIBLE
- }
+ appVersionMenu.updateAvailable = isOutdated || !isSupported
+ appVersionMenu.version = versionInfoCache?.version ?: ""
}
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SplitTunnellingFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SplitTunnellingFragment.kt
index dc905abdbf..9bce3cb620 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SplitTunnellingFragment.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SplitTunnellingFragment.kt
@@ -11,7 +11,9 @@ import android.view.View
import android.view.ViewGroup
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.applist.AppListAdapter
+import net.mullvad.mullvadvpn.ui.widget.CellSwitch
import net.mullvad.mullvadvpn.ui.widget.CustomRecyclerView
+import net.mullvad.mullvadvpn.ui.widget.ToggleCell
import net.mullvad.mullvadvpn.util.AdapterWithHeader
class SplitTunnellingFragment : ServiceDependentFragment(OnNoService.GoToLaunchScreen) {
@@ -41,7 +43,7 @@ class SplitTunnellingFragment : ServiceDependentFragment(OnNoService.GoToLaunchS
}
private lateinit var appListAdapter: AppListAdapter
- private lateinit var enabledToggle: CellSwitch
+ private lateinit var enabledToggle: ToggleCell
private lateinit var excludeApplicationsFadeOut: ObjectAnimator
private lateinit var loadingSpinnerFadeIn: ObjectAnimator
private lateinit var titleController: CollapsibleTitleController
@@ -117,7 +119,7 @@ class SplitTunnellingFragment : ServiceDependentFragment(OnNoService.GoToLaunchS
}
}
- enabledToggle = header.findViewById<CellSwitch>(R.id.enabled_toggle).apply {
+ enabledToggle = header.findViewById<ToggleCell>(R.id.enabled).apply {
if (splitTunnelling.enabled) {
forcefullySetState(CellSwitch.State.ON)
} else {
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountCell.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountCell.kt
new file mode 100644
index 0000000000..15c860ef00
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountCell.kt
@@ -0,0 +1,76 @@
+package net.mullvad.mullvadvpn.ui.widget
+
+import android.content.Context
+import android.graphics.Typeface
+import android.util.AttributeSet
+import android.util.TypedValue
+import android.view.Gravity
+import android.widget.TextView
+import kotlin.properties.Delegates.observable
+import net.mullvad.mullvadvpn.R
+import net.mullvad.mullvadvpn.util.TimeLeftFormatter
+import org.joda.time.DateTime
+import org.joda.time.Duration
+
+class AccountCell : NavigateCell {
+ private val formatter = TimeLeftFormatter(resources)
+
+ private val expiredColor = context.getColor(R.color.red)
+ private val normalColor = context.getColor(R.color.white60)
+
+ private val remainingTimeLabel = TextView(context).apply {
+ layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0.0f)
+ gravity = Gravity.RIGHT
+
+ resources.getDimensionPixelSize(R.dimen.cell_label_horizontal_padding).let { padding ->
+ setPadding(padding, 0, padding, 0)
+ }
+
+ setAllCaps(true)
+ setTextColor(normalColor)
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, 13.0f)
+ setTypeface(null, Typeface.BOLD)
+
+ text = ""
+ }
+
+ var accountExpiry by observable<DateTime?>(null) { _, _, expiry ->
+ remainingTimeLabel.apply {
+ if (expiry != null) {
+ val remainingTime = Duration(DateTime.now(), expiry)
+
+ if (remainingTime.isShorterThan(Duration.ZERO)) {
+ setText(R.string.out_of_time)
+ setTextColor(expiredColor)
+ } else {
+ setText(formatter.format(expiry, remainingTime))
+ setTextColor(normalColor)
+ }
+ } else {
+ text = ""
+ }
+ }
+ }
+
+ constructor(context: Context) : super(context) {}
+
+ constructor(context: Context, attributes: AttributeSet) : super(context, attributes) {}
+
+ constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) :
+ super(context, attributes, defaultStyleAttribute) {}
+
+ constructor(
+ context: Context,
+ attributes: AttributeSet,
+ defaultStyleAttribute: Int,
+ defaultStyleResource: Int
+ ) : super(context, attributes, defaultStyleAttribute, defaultStyleResource) {}
+
+ init {
+ cell.addView(remainingTimeLabel, cell.childCount - 1)
+ }
+
+ private fun getRemainingText(pluralId: Int, quantity: Int): String {
+ return resources.getQuantityString(pluralId, quantity, quantity)
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AppVersionCell.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AppVersionCell.kt
new file mode 100644
index 0000000000..cba7095eda
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AppVersionCell.kt
@@ -0,0 +1,90 @@
+package net.mullvad.mullvadvpn.ui.widget
+
+import android.content.Context
+import android.content.Intent
+import android.graphics.Typeface
+import android.net.Uri
+import android.util.AttributeSet
+import android.util.TypedValue
+import android.view.Gravity
+import android.widget.ImageView
+import android.widget.TextView
+import kotlin.properties.Delegates.observable
+import net.mullvad.mullvadvpn.R
+
+class AppVersionCell : Cell {
+ private val warningIcon = ImageView(context).apply {
+ layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0.0f)
+
+ resources.getDimensionPixelSize(R.dimen.cell_label_horizontal_padding).let { padding ->
+ setPadding(padding, 0, 0, 0)
+ }
+
+ setImageResource(R.drawable.icon_alert)
+ }
+
+ private val versionLabel = TextView(context).apply {
+ layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0.0f)
+ gravity = Gravity.RIGHT
+
+ resources.getDimensionPixelSize(R.dimen.cell_label_horizontal_padding).let { padding ->
+ setPadding(padding, 0, padding, 0)
+ }
+
+ setTextColor(context.getColor(R.color.white60))
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, 13.0f)
+ setTypeface(null, Typeface.BOLD)
+
+ text = ""
+ }
+
+ private val externalLinkIcon = ImageView(context).apply {
+ layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0.0f)
+ alpha = 0.6f
+
+ setImageResource(R.drawable.icon_extlink)
+ }
+
+ var updateAvailable by observable(false) { _, _, updateAvailable ->
+ if (updateAvailable) {
+ warningIcon.visibility = VISIBLE
+ footer?.visibility = VISIBLE
+ } else {
+ warningIcon.visibility = GONE
+ footer?.visibility = GONE
+ }
+ }
+
+ var version by observable("") { _, _, version ->
+ versionLabel.text = version
+ }
+
+ constructor(context: Context) : super(context) {}
+
+ constructor(context: Context, attributes: AttributeSet) : super(context, attributes) {}
+
+ constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) :
+ super(context, attributes, defaultStyleAttribute) {}
+
+ constructor(
+ context: Context,
+ attributes: AttributeSet,
+ defaultStyleAttribute: Int,
+ defaultStyleResource: Int
+ ) : super(context, attributes, defaultStyleAttribute, defaultStyleResource) {}
+
+ init {
+ cell.addView(warningIcon, 0)
+ cell.addView(versionLabel)
+ cell.addView(externalLinkIcon)
+
+ onClickListener = { openLink() }
+ }
+
+ private fun openLink() {
+ val url = context.getString(R.string.download_url)
+ val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
+
+ context.startActivity(intent)
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Cell.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Cell.kt
new file mode 100644
index 0000000000..2d40426ca4
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Cell.kt
@@ -0,0 +1,134 @@
+package net.mullvad.mullvadvpn.ui.widget
+
+import android.content.Context
+import android.graphics.Typeface
+import android.util.AttributeSet
+import android.util.TypedValue
+import android.view.Gravity
+import android.widget.LinearLayout
+import android.widget.TextView
+import net.mullvad.mullvadvpn.R
+
+open class Cell : LinearLayout {
+ private val label = TextView(context).apply {
+ val horizontalPadding =
+ resources.getDimensionPixelSize(R.dimen.cell_label_horizontal_padding)
+ val verticalPadding = resources.getDimensionPixelSize(R.dimen.cell_label_vertical_padding)
+
+ layoutParams = LayoutParams(0, LayoutParams.WRAP_CONTENT, 1.0f)
+ setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding)
+
+ setTextColor(context.getColor(R.color.white))
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, 20.0f)
+ setTypeface(null, Typeface.BOLD)
+ }
+
+ protected var footer: TextView? = null
+ set(value) {
+ field = value?.apply {
+ val horizontalPadding =
+ resources.getDimensionPixelSize(R.dimen.cell_footer_horizontal_padding)
+ val topPadding = resources.getDimensionPixelSize(R.dimen.cell_footer_top_padding)
+
+ layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
+ setPadding(horizontalPadding, topPadding, horizontalPadding, 0)
+
+ setTextColor(context.getColor(R.color.white60))
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, 13.0f)
+ }
+ }
+
+ protected var cell: LinearLayout = this
+ set(value) {
+ field = value.apply {
+ isClickable = true
+ gravity = Gravity.CENTER
+ orientation = HORIZONTAL
+
+ setBackgroundResource(R.drawable.cell_button_background)
+
+ resources.getDimensionPixelSize(R.dimen.cell_horizontal_padding).let { padding ->
+ setPadding(padding, 0, padding, 0)
+ }
+
+ addView(label)
+
+ setOnClickListener { onClickListener?.invoke() }
+ }
+ }
+
+ var onClickListener: (() -> Unit)? = null
+
+ constructor(context: Context, footer: TextView? = null) : super(context) {
+ this.footer = footer
+ }
+
+ constructor(context: Context, attributes: AttributeSet, footer: TextView? = null) :
+ super(context, attributes) {
+ this.footer = footer
+ loadAttributes(attributes)
+ }
+
+ constructor(
+ context: Context,
+ attributes: AttributeSet,
+ defaultStyleAttribute: Int,
+ footer: TextView? = null
+ ) : super(context, attributes, defaultStyleAttribute) {
+ this.footer = footer
+ loadAttributes(attributes)
+ }
+
+ constructor(
+ context: Context,
+ attributes: AttributeSet,
+ defaultStyleAttribute: Int,
+ defaultStyleResource: Int,
+ footer: TextView? = null
+ ) : super(context, attributes, defaultStyleAttribute, defaultStyleResource) {
+ this.footer = footer
+ loadAttributes(attributes)
+ }
+
+ private fun loadAttributes(attributes: AttributeSet) {
+ context.theme.obtainStyledAttributes(attributes, R.styleable.TextAttribute, 0, 0).apply {
+ try {
+ label.text = getString(R.styleable.TextAttribute_text) ?: ""
+ } finally {
+ recycle()
+ }
+ }
+
+ context.theme.obtainStyledAttributes(attributes, R.styleable.Cell, 0, 0).apply {
+ try {
+ getString(R.styleable.Cell_footer)?.let { footerText ->
+ if (footer == null) {
+ footer = TextView(context)
+ }
+
+ footer?.text = footerText
+ }
+ } finally {
+ recycle()
+ }
+ }
+
+ setUp()
+ }
+
+ private fun setUp() {
+ if (footer != null) {
+ cell = LinearLayout(context).apply {
+ layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
+ }
+
+ isClickable = false
+ orientation = VERTICAL
+
+ addView(cell)
+ addView(footer)
+ } else {
+ cell = this
+ }
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/CellSwitch.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/CellSwitch.kt
index b01a73b3e3..dee5c59e49 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/CellSwitch.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/CellSwitch.kt
@@ -1,4 +1,4 @@
-package net.mullvad.mullvadvpn.ui
+package net.mullvad.mullvadvpn.ui.widget
import android.animation.ValueAnimator
import android.content.Context
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/MtuCell.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/MtuCell.kt
new file mode 100644
index 0000000000..eb1463cd45
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/MtuCell.kt
@@ -0,0 +1,91 @@
+package net.mullvad.mullvadvpn.ui.widget
+
+import android.content.Context
+import android.text.Editable
+import android.text.TextWatcher
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.EditText
+import android.widget.TextView
+import kotlin.properties.Delegates.observable
+import net.mullvad.mullvadvpn.R
+
+private const val MIN_MTU_VALUE = 1280
+private const val MAX_MTU_VALUE = 1420
+
+class MtuCell : Cell {
+ private val input =
+ (LayoutInflater.from(context).inflate(R.layout.mtu_edit_text, null) as EditText).apply {
+ val width = resources.getDimensionPixelSize(R.dimen.cell_input_width)
+ val height = resources.getDimensionPixelSize(R.dimen.cell_input_height)
+
+ layoutParams = LayoutParams(width, height, 0.0f)
+
+ addTextChangedListener(InputWatcher())
+ setOnFocusChangeListener { _, newHasFocus -> hasFocus = newHasFocus }
+ }
+
+ private val validInputColor = context.getColor(R.color.white)
+ private val invalidInputColor = context.getColor(R.color.red)
+
+ var value: Int?
+ get() = input.text.toString().trim().toIntOrNull()
+ set(value) = input.setText(value?.toString() ?: "")
+
+ var onSubmit: ((Int?) -> Unit)? = null
+
+ var hasFocus by observable(false) { _, oldValue, newValue ->
+ if (oldValue == true && newValue == false) {
+ val mtu = value
+
+ if (mtu == null || (mtu >= MIN_MTU_VALUE && mtu <= MAX_MTU_VALUE)) {
+ onSubmit?.invoke(mtu)
+ }
+ }
+ }
+
+ constructor(context: Context) : super(context, TextView(context)) {}
+
+ constructor(context: Context, attributes: AttributeSet) :
+ super(context, attributes, TextView(context)) {}
+
+ constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) :
+ super(context, attributes, defaultStyleAttribute, TextView(context)) {}
+
+ constructor(
+ context: Context,
+ attributes: AttributeSet,
+ defaultStyleAttribute: Int,
+ defaultStyleResource: Int
+ ) : super(
+ context,
+ attributes,
+ defaultStyleAttribute,
+ defaultStyleResource,
+ TextView(context)
+ ) {}
+
+ init {
+ cell.setEnabled(false)
+ cell.addView(input)
+
+ footer?.text =
+ context.getString(R.string.wireguard_mtu_footer, MIN_MTU_VALUE, MAX_MTU_VALUE)
+ }
+
+ inner class InputWatcher : TextWatcher {
+ override fun beforeTextChanged(text: CharSequence, start: Int, count: Int, after: Int) {}
+
+ override fun onTextChanged(text: CharSequence, start: Int, count: Int, after: Int) {}
+
+ override fun afterTextChanged(text: Editable) {
+ val value = text.toString().trim().toIntOrNull()
+
+ if (value != null && value >= MIN_MTU_VALUE && value <= MAX_MTU_VALUE) {
+ input.setTextColor(validInputColor)
+ } else {
+ input.setTextColor(invalidInputColor)
+ }
+ }
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NavigateCell.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NavigateCell.kt
new file mode 100644
index 0000000000..9ff9c3588d
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NavigateCell.kt
@@ -0,0 +1,60 @@
+package net.mullvad.mullvadvpn.ui.widget
+
+import android.content.Context
+import android.support.v4.app.Fragment
+import android.support.v4.app.FragmentActivity
+import android.util.AttributeSet
+import android.widget.ImageView
+import kotlin.reflect.KClass
+import net.mullvad.mullvadvpn.R
+
+open class NavigateCell : Cell {
+ private val chevron = ImageView(context).apply {
+ val width = resources.getDimensionPixelSize(R.dimen.chevron_width)
+ val height = resources.getDimensionPixelSize(R.dimen.chevron_height)
+
+ layoutParams = LayoutParams(width, height, 0.0f)
+ alpha = 0.6f
+
+ setImageResource(R.drawable.icon_chevron)
+ }
+
+ var targetFragment: KClass<out Fragment>? = null
+
+ constructor(context: Context) : super(context) {}
+
+ constructor(context: Context, attributes: AttributeSet) : super(context, attributes) {}
+
+ constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) :
+ super(context, attributes, defaultStyleAttribute) {}
+
+ constructor(
+ context: Context,
+ attributes: AttributeSet,
+ defaultStyleAttribute: Int,
+ defaultStyleResource: Int
+ ) : super(context, attributes, defaultStyleAttribute, defaultStyleResource) {}
+
+ init {
+ cell.addView(chevron)
+ onClickListener = { openSubFragment() }
+ }
+
+ private fun openSubFragment() {
+ targetFragment?.let { fragmentClass ->
+ val fragment = fragmentClass.java.getConstructor().newInstance()
+
+ (context as? FragmentActivity)?.supportFragmentManager?.beginTransaction()?.apply {
+ setCustomAnimations(
+ R.anim.fragment_enter_from_right,
+ R.anim.fragment_half_exit_to_left,
+ R.anim.fragment_half_enter_from_left,
+ R.anim.fragment_exit_to_right
+ )
+ replace(R.id.main_fragment, fragment)
+ addToBackStack(null)
+ commit()
+ }
+ }
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/ToggleCell.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/ToggleCell.kt
new file mode 100644
index 0000000000..cde050bbc2
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/ToggleCell.kt
@@ -0,0 +1,45 @@
+package net.mullvad.mullvadvpn.ui.widget
+
+import android.content.Context
+import android.util.AttributeSet
+
+class ToggleCell : Cell {
+ private val toggle = CellSwitch(context).apply {
+ layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0.0f)
+ }
+
+ var state
+ get() = toggle.state
+ set(value) { toggle.state = value }
+
+ var listener
+ get() = toggle.listener
+ set(value) { toggle.listener = value }
+
+ constructor(context: Context) : super(context) {}
+
+ constructor(context: Context, attributes: AttributeSet) : super(context, attributes) {}
+
+ constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) :
+ super(context, attributes, defaultStyleAttribute) {}
+
+ constructor(
+ context: Context,
+ attributes: AttributeSet,
+ defaultStyleAttribute: Int,
+ defaultStyleResource: Int
+ ) : super(context, attributes, defaultStyleAttribute, defaultStyleResource) {}
+
+ init {
+ onClickListener = { toggle() }
+ cell.addView(toggle)
+ }
+
+ fun toggle() {
+ toggle.toggle()
+ }
+
+ fun forcefullySetState(state: CellSwitch.State) {
+ toggle.forcefullySetState(state)
+ }
+}
diff --git a/android/src/main/res/layout/advanced.xml b/android/src/main/res/layout/advanced.xml
index 3aef5882b7..f9640e956d 100644
--- a/android/src/main/res/layout/advanced.xml
+++ b/android/src/main/res/layout/advanced.xml
@@ -42,91 +42,21 @@
android:layout_marginLeft="24dp"
android:text="@string/settings_advanced"
style="@style/SettingsExpandedHeader" />
- <LinearLayout android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="24dp"
- android:paddingHorizontal="16dp"
- android:background="@drawable/cell_button_background"
- android:gravity="center">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingHorizontal="8dp"
- android:paddingVertical="17dp"
- android:textColor="@color/white"
- android:textSize="20sp"
- android:textStyle="bold"
- android:text="@string/wireguard_mtu" />
- <EditText android:id="@+id/wireguard_mtu_input"
- android:layout_width="80dp"
- android:layout_height="34dp"
- android:layout_weight="0"
- android:paddingHorizontal="4dp"
- android:background="@drawable/cell_input_background"
- android:digits="0123456789"
- android:inputType="number"
- android:singleLine="true"
- android:imeOptions="flagNoPersonalizedLearning"
- android:textCursorDrawable="@drawable/cell_input_cursor"
- android:gravity="center"
- android:hint="@string/hint_default"
- android:textColorHint="@color/white80"
- android:textColor="@color/white"
- android:textSize="20sp" />
- </LinearLayout>
- <TextView android:id="@+id/wireguard_mtu_footer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:paddingHorizontal="24dp"
- android:textColor="@color/white60"
- android:textSize="13sp" />
- <LinearLayout android:id="@+id/wireguard_keys"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="24dp"
- android:paddingHorizontal="16dp"
- android:background="@drawable/cell_button_background"
- android:clickable="true"
- android:gravity="center">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingHorizontal="8dp"
- android:paddingVertical="17dp"
- android:textColor="@color/white"
- android:textSize="20sp"
- android:textStyle="bold"
- android:text="@string/wireguard_key" />
- <ImageView android:layout_width="14dp"
- android:layout_height="24dp"
- android:layout_weight="0"
- android:alpha="0.6"
- android:src="@drawable/icon_chevron" />
- </LinearLayout>
- <LinearLayout android:id="@+id/split_tunnelling"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="24dp"
- android:paddingHorizontal="16dp"
- android:background="@drawable/cell_button_background"
- android:clickable="true"
- android:gravity="center">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingHorizontal="8dp"
- android:paddingVertical="17dp"
- android:textColor="@color/white"
- android:textSize="20sp"
- android:textStyle="bold"
- android:text="@string/split_tunnelling" />
- <ImageView android:layout_width="14dp"
- android:layout_height="24dp"
- android:layout_weight="0"
- android:alpha="0.6"
- android:src="@drawable/icon_chevron" />
- </LinearLayout>
+ <net.mullvad.mullvadvpn.ui.widget.MtuCell android:id="@+id/wireguard_mtu"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="24dp"
+ mullvad:text="@string/wireguard_mtu" />
+ <net.mullvad.mullvadvpn.ui.widget.NavigateCell android:id="@+id/wireguard_keys"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="24dp"
+ mullvad:text="@string/wireguard_key" />
+ <net.mullvad.mullvadvpn.ui.widget.NavigateCell android:id="@+id/split_tunnelling"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="24dp"
+ mullvad:text="@string/split_tunnelling" />
</LinearLayout>
</net.mullvad.mullvadvpn.ui.widget.ListenableScrollView>
</LinearLayout>
diff --git a/android/src/main/res/layout/app_list_item.xml b/android/src/main/res/layout/app_list_item.xml
index 741f3220de..2b32a160ae 100644
--- a/android/src/main/res/layout/app_list_item.xml
+++ b/android/src/main/res/layout/app_list_item.xml
@@ -30,8 +30,8 @@
android:textColor="@color/white"
android:textSize="16sp"
android:text="" />
- <net.mullvad.mullvadvpn.ui.CellSwitch android:id="@+id/excluded"
- android:layout_width="52dp"
- android:layout_height="32dp"
- android:layout_weight="0" />
+ <net.mullvad.mullvadvpn.ui.widget.CellSwitch android:id="@+id/excluded"
+ android:layout_width="52dp"
+ android:layout_height="32dp"
+ android:layout_weight="0" />
</LinearLayout>
diff --git a/android/src/main/res/layout/mtu_edit_text.xml b/android/src/main/res/layout/mtu_edit_text.xml
new file mode 100644
index 0000000000..05fce42e7d
--- /dev/null
+++ b/android/src/main/res/layout/mtu_edit_text.xml
@@ -0,0 +1,13 @@
+<EditText xmlns:android="http://schemas.android.com/apk/res/android"
+ android:paddingHorizontal="4dp"
+ android:background="@drawable/cell_input_background"
+ android:digits="0123456789"
+ android:inputType="number"
+ android:singleLine="true"
+ android:imeOptions="flagNoPersonalizedLearning"
+ android:textCursorDrawable="@drawable/cell_input_cursor"
+ android:gravity="center"
+ android:hint="@string/hint_default"
+ android:textColorHint="@color/white80"
+ android:textColor="@color/white"
+ android:textSize="20sp" />
diff --git a/android/src/main/res/layout/preferences.xml b/android/src/main/res/layout/preferences.xml
index 0f513be296..3456c22954 100644
--- a/android/src/main/res/layout/preferences.xml
+++ b/android/src/main/res/layout/preferences.xml
@@ -42,64 +42,18 @@
android:layout_marginLeft="24dp"
android:text="@string/settings_preferences"
style="@style/SettingsExpandedHeader" />
- <LinearLayout android:id="@+id/auto_connect"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="24dp"
- android:paddingHorizontal="16dp"
- android:background="@drawable/cell_button_background"
- android:gravity="center">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingHorizontal="8dp"
- android:paddingVertical="17dp"
- android:textColor="@color/white"
- android:textSize="20sp"
- android:textStyle="bold"
- android:text="@string/auto_connect" />
- <net.mullvad.mullvadvpn.ui.CellSwitch android:id="@+id/auto_connect_toggle"
- android:layout_width="52dp"
- android:layout_height="32dp"
- android:layout_weight="0" />
- </LinearLayout>
- <TextView android:id="@+id/auto_connect_footer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:paddingHorizontal="24dp"
- android:textColor="@color/white60"
- android:textSize="13sp"
- android:text="@string/auto_connect_footer" />
- <LinearLayout android:id="@+id/allow_lan"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="24dp"
- android:paddingHorizontal="16dp"
- android:background="@drawable/cell_button_background"
- android:gravity="center">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingHorizontal="8dp"
- android:paddingVertical="17dp"
- android:textColor="@color/white"
- android:textSize="20sp"
- android:textStyle="bold"
- android:text="@string/local_network_sharing" />
- <net.mullvad.mullvadvpn.ui.CellSwitch android:id="@+id/allow_lan_toggle"
- android:layout_width="52dp"
- android:layout_height="32dp"
- android:layout_weight="0" />
- </LinearLayout>
- <TextView android:id="@+id/allow_lan_footer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:paddingHorizontal="24dp"
- android:textColor="@color/white60"
- android:textSize="13sp"
- android:text="@string/allow_lan_footer" />
+ <net.mullvad.mullvadvpn.ui.widget.ToggleCell android:id="@+id/auto_connect"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="24dp"
+ mullvad:text="@string/auto_connect"
+ mullvad:footer="@string/auto_connect_footer" />
+ <net.mullvad.mullvadvpn.ui.widget.ToggleCell android:id="@+id/allow_lan"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="24dp"
+ mullvad:text="@string/local_network_sharing"
+ mullvad:footer="@string/allow_lan_footer" />
</LinearLayout>
</net.mullvad.mullvadvpn.ui.widget.ListenableScrollView>
</LinearLayout>
diff --git a/android/src/main/res/layout/settings.xml b/android/src/main/res/layout/settings.xml
index da849c4144..b356c7c7be 100644
--- a/android/src/main/res/layout/settings.xml
+++ b/android/src/main/res/layout/settings.xml
@@ -1,4 +1,5 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:mullvad="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/darkBlue"
@@ -42,159 +43,32 @@
android:layout_marginLeft="24dp"
android:text="@string/settings"
style="@style/SettingsExpandedHeader" />
- <LinearLayout android:id="@+id/account"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="24dp"
- android:paddingHorizontal="16dp"
- android:background="@drawable/cell_button_background"
- android:clickable="true"
- android:gravity="center"
- android:visibility="gone">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingHorizontal="8dp"
- android:paddingVertical="17dp"
- android:textColor="@color/white"
- android:textSize="20sp"
- android:textStyle="bold"
- android:text="@string/settings_account" />
- <TextView android:id="@+id/remaining_time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingHorizontal="8dp"
- android:gravity="right"
- android:textColor="@color/white60"
- android:textSize="13sp"
- android:textStyle="bold"
- android:text=""
- android:textAllCaps="true" />
- <ImageView android:layout_width="14dp"
- android:layout_height="24dp"
- android:layout_weight="0"
- android:alpha="0.6"
- android:src="@drawable/icon_chevron" />
- </LinearLayout>
- <LinearLayout android:id="@+id/preferences"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="1dp"
- android:paddingHorizontal="16dp"
- android:background="@drawable/cell_button_background"
- android:clickable="true"
- android:gravity="center"
- android:visibility="gone">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingHorizontal="8dp"
- android:paddingVertical="17dp"
- android:textColor="@color/white"
- android:textSize="20sp"
- android:textStyle="bold"
- android:text="@string/settings_preferences" />
- <ImageView android:layout_width="14dp"
- android:layout_height="24dp"
- android:layout_weight="0"
- android:alpha="0.6"
- android:src="@drawable/icon_chevron" />
- </LinearLayout>
- <LinearLayout android:id="@+id/advanced"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="1dp"
- android:paddingHorizontal="16dp"
- android:background="@drawable/cell_button_background"
- android:clickable="true"
- android:gravity="center"
- android:visibility="gone">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingHorizontal="8dp"
- android:paddingVertical="17dp"
- android:textColor="@color/white"
- android:textSize="20sp"
- android:textStyle="bold"
- android:text="@string/settings_advanced" />
- <ImageView android:layout_width="14dp"
- android:layout_height="24dp"
- android:layout_weight="0"
- android:alpha="0.6"
- android:src="@drawable/icon_chevron" />
- </LinearLayout>
- <LinearLayout android:id="@+id/app_version"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="24dp"
- android:paddingHorizontal="16dp"
- android:background="@drawable/cell_button_background"
- android:clickable="true"
- android:gravity="center">
- <ImageView android:id="@+id/app_version_warning"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:paddingLeft="8dp"
- android:src="@drawable/icon_alert" />
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingHorizontal="8dp"
- android:paddingVertical="17dp"
- android:textColor="@color/white"
- android:textSize="20sp"
- android:textStyle="bold"
- android:text="@string/app_version" />
- <TextView android:id="@+id/app_version_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingHorizontal="8dp"
- android:gravity="right"
- android:textColor="@color/white60"
- android:textSize="13sp"
- android:textStyle="bold"
- android:text="" />
- <ImageView android:layout_width="16dp"
- android:layout_height="16dp"
- android:layout_weight="0"
- android:alpha="0.6"
- android:src="@drawable/icon_extlink" />
- </LinearLayout>
- <TextView android:id="@+id/app_version_footer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:paddingHorizontal="24dp"
- android:textColor="@color/white60"
- android:textSize="13sp"
- android:text="@string/update_available_footer" />
- <LinearLayout android:id="@+id/report_a_problem"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="24dp"
- android:paddingHorizontal="16dp"
- android:background="@drawable/cell_button_background"
- android:clickable="true"
- android:gravity="center">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingHorizontal="8dp"
- android:paddingVertical="17dp"
- android:textColor="@color/white"
- android:textSize="20sp"
- android:textStyle="bold"
- android:text="@string/report_a_problem" />
- <ImageView android:layout_width="14dp"
- android:layout_height="24dp"
- android:layout_weight="0"
- android:alpha="0.6"
- android:src="@drawable/icon_chevron" />
- </LinearLayout>
+ <net.mullvad.mullvadvpn.ui.widget.AccountCell android:id="@+id/account"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="24dp"
+ mullvad:text="@string/settings_account" />
+ <net.mullvad.mullvadvpn.ui.widget.NavigateCell android:id="@+id/preferences"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="1dp"
+ mullvad:text="@string/settings_preferences" />
+ <net.mullvad.mullvadvpn.ui.widget.NavigateCell android:id="@+id/advanced"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="1dp"
+ mullvad:text="@string/settings_advanced" />
+ <net.mullvad.mullvadvpn.ui.widget.AppVersionCell android:id="@+id/app_version"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="1dp"
+ mullvad:text="@string/app_version"
+ mullvad:footer="@string/update_available_footer" />
+ <net.mullvad.mullvadvpn.ui.widget.NavigateCell android:id="@+id/report_a_problem"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="24dp"
+ mullvad:text="@string/report_a_problem" />
<Button android:id="@+id/quit_button"
android:layout_marginTop="24dp"
android:layout_marginLeft="24dp"
diff --git a/android/src/main/res/layout/split_tunnelling_header.xml b/android/src/main/res/layout/split_tunnelling_header.xml
index 3fa595f55f..f15f573651 100644
--- a/android/src/main/res/layout/split_tunnelling_header.xml
+++ b/android/src/main/res/layout/split_tunnelling_header.xml
@@ -1,4 +1,5 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:mullvad="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
@@ -19,28 +20,11 @@
android:text="@string/split_tunnelling_description"
android:textColor="@color/white60"
android:textSize="13sp" />
- <LinearLayout android:id="@+id/enabled"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="24dp"
- android:paddingHorizontal="16dp"
- android:background="@drawable/cell_button_background"
- android:gravity="center"
- android:clickable="true">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingHorizontal="8dp"
- android:paddingVertical="17dp"
- android:textColor="@color/white"
- android:textSize="20sp"
- android:textStyle="bold"
- android:text="@string/enabled" />
- <net.mullvad.mullvadvpn.ui.CellSwitch android:id="@+id/enabled_toggle"
- android:layout_width="52dp"
- android:layout_height="32dp"
- android:layout_weight="0" />
- </LinearLayout>
+ <net.mullvad.mullvadvpn.ui.widget.ToggleCell android:id="@+id/enabled"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="24dp"
+ mullvad:text="@string/enabled" />
<LinearLayout android:id="@+id/exclude_applications"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/android/src/main/res/values/attrs.xml b/android/src/main/res/values/attrs.xml
index 1ddae37ee7..f0f573d138 100644
--- a/android/src/main/res/values/attrs.xml
+++ b/android/src/main/res/values/attrs.xml
@@ -14,6 +14,10 @@
<attr name="showSpinner"
format="boolean" />
</declare-styleable>
+ <declare-styleable name="Cell">
+ <attr name="footer"
+ format="reference|string" />
+ </declare-styleable>
<declare-styleable name="CopyableInformationView">
<attr name="clipboardLabel"
format="reference|string" />
diff --git a/android/src/main/res/values/dimensions.xml b/android/src/main/res/values/dimensions.xml
index 99a79846f5..c4ebc722ad 100644
--- a/android/src/main/res/values/dimensions.xml
+++ b/android/src/main/res/values/dimensions.xml
@@ -9,10 +9,19 @@
<dimen name="normal_button_height">44dp</dimen>
<dimen name="tall_button_height">64dp</dimen>
<dimen name="cell_switch_border_radius">16dp</dimen>
- <dimen name="cell_switch_width">32dp</dimen>
- <dimen name="cell_switch_height">52dp</dimen>
+ <dimen name="cell_switch_width">52dp</dimen>
+ <dimen name="cell_switch_height">32dp</dimen>
<dimen name="cell_switch_knob_margin">4dp</dimen>
<dimen name="cell_switch_knob_max_translation">20dp</dimen>
<dimen name="cell_switch_knob_size">24dp</dimen>
<dimen name="settings_back_button_padding">12dp</dimen>
+ <dimen name="cell_horizontal_padding">16dp</dimen>
+ <dimen name="cell_label_horizontal_padding">8dp</dimen>
+ <dimen name="cell_label_vertical_padding">17dp</dimen>
+ <dimen name="cell_input_width">80dp</dimen>
+ <dimen name="cell_input_height">34dp</dimen>
+ <dimen name="cell_footer_top_padding">8dp</dimen>
+ <dimen name="cell_footer_horizontal_padding">24dp</dimen>
+ <dimen name="chevron_width">14dp</dimen>
+ <dimen name="chevron_height">24dp</dimen>
</resources>