diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-07-28 11:14:24 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-07-28 11:14:24 -0300 |
| commit | 027f0c14685ab944b5313e846ab9e132d5b192d2 (patch) | |
| tree | cc85f4a6f229e35d29848364ae33248d18caad4a /android | |
| parent | ee608498b17b789d18070e31328a5d50ed16eb53 (diff) | |
| parent | 4428e1fe3226924e5b879ddcff90ef95e22714ca (diff) | |
| download | mullvadvpn-027f0c14685ab944b5313e846ab9e132d5b192d2.tar.xz mullvadvpn-027f0c14685ab944b5313e846ab9e132d5b192d2.zip | |
Merge branch 'create-cell-widget'
Diffstat (limited to 'android')
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> |
