summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md3
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/SettingsListener.kt18
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/model/Settings.kt6
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt5
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/CellSwitch.kt220
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/PreferencesFragment.kt65
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt9
-rw-r--r--android/src/main/res/drawable/cell_switch_background.xml27
-rw-r--r--android/src/main/res/layout/preferences.xml78
-rw-r--r--android/src/main/res/layout/settings.xml35
-rw-r--r--android/src/main/res/values/dimensions.xml6
-rw-r--r--android/src/main/res/values/strings.xml6
-rw-r--r--mullvad-jni/src/daemon_interface.rs8
-rw-r--r--mullvad-jni/src/from_java.rs13
-rw-r--r--mullvad-jni/src/lib.rs22
-rw-r--r--mullvad-types/src/settings/mod.rs1
16 files changed, 515 insertions, 7 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c155f988f2..6155667724 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,9 @@ Line wrap the file at 100 chars. Th
- Add reconnect button to the desktop app
- Add monochrome option for the tray icon on Windows and Linux.
+#### Android
+- Add option to enable or disable local network sharing.
+
### Changed
- Change project copyright and company name from Amagicom AB to Mullvad VPN AB
- Only reconnect when settings change if a relevant tunnel protocol is used.
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/SettingsListener.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/SettingsListener.kt
index a822526890..be27eb7b89 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/SettingsListener.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/SettingsListener.kt
@@ -9,7 +9,8 @@ class SettingsListener(val daemon: MullvadDaemon) {
maybeSettings?.let { settings -> handleNewSettings(settings) }
}
- private var settings: Settings? = null
+ var settings: Settings? = null
+ private set
var onAccountNumberChange: ((String?) -> Unit)? = null
set(value) {
@@ -19,6 +20,17 @@ class SettingsListener(val daemon: MullvadDaemon) {
}
}
+ var onAllowLanChange: ((Boolean) -> Unit)? = null
+ set(value) {
+ synchronized(this) {
+ field = value
+
+ settings?.let { safeSettings ->
+ value?.invoke(safeSettings.allowLan)
+ }
+ }
+ }
+
var onRelaySettingsChange: ((RelaySettings?) -> Unit)? = null
set(value) {
synchronized(this) {
@@ -43,6 +55,10 @@ class SettingsListener(val daemon: MullvadDaemon) {
onRelaySettingsChange?.invoke(newSettings.relaySettings)
}
+ if (settings?.allowLan != newSettings.allowLan) {
+ onAllowLanChange?.invoke(newSettings.allowLan)
+ }
+
settings = newSettings
}
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/Settings.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/Settings.kt
index 9e4985820f..6cc83b157f 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/Settings.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/Settings.kt
@@ -1,3 +1,7 @@
package net.mullvad.mullvadvpn.model
-data class Settings(var accountToken: String?, var relaySettings: RelaySettings)
+data class Settings(
+ var accountToken: String?,
+ var relaySettings: RelaySettings,
+ var allowLan: Boolean
+)
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt
index 8e8806cc70..e61567a0fe 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt
@@ -81,6 +81,10 @@ class MullvadDaemon(val vpnService: MullvadVpnService) {
setAccount(daemonInterfaceAddress, accountToken)
}
+ fun setAllowLan(allowLan: Boolean) {
+ setAllowLan(daemonInterfaceAddress, allowLan)
+ }
+
fun shutdown() {
shutdown(daemonInterfaceAddress)
}
@@ -112,6 +116,7 @@ class MullvadDaemon(val vpnService: MullvadVpnService) {
private external fun getVersionInfo(daemonInterfaceAddress: Long): AppVersionInfo?
private external fun getWireguardKey(daemonInterfaceAddress: Long): PublicKey?
private external fun setAccount(daemonInterfaceAddress: Long, accountToken: String?)
+ private external fun setAllowLan(daemonInterfaceAddress: Long, allowLan: Boolean)
private external fun shutdown(daemonInterfaceAddress: Long)
private external fun updateRelaySettings(
daemonInterfaceAddress: Long,
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/CellSwitch.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/CellSwitch.kt
new file mode 100644
index 0000000000..0aba6ce947
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/CellSwitch.kt
@@ -0,0 +1,220 @@
+package net.mullvad.mullvadvpn.ui
+
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.Paint.Style
+import android.graphics.drawable.ShapeDrawable
+import android.graphics.drawable.shapes.OvalShape
+import android.util.AttributeSet
+import android.view.GestureDetector
+import android.view.GestureDetector.OnGestureListener
+import android.view.Gravity
+import android.view.MotionEvent
+import android.widget.ImageView
+import android.widget.LinearLayout
+import net.mullvad.mullvadvpn.R
+
+class CellSwitch : LinearLayout {
+ enum class State {
+ ON,
+ OFF
+ }
+
+ var state = State.OFF
+ set(value) {
+ if (field != value) {
+ field = value
+ animateToState()
+ listener?.invoke(value)
+ }
+ }
+
+ var listener: ((State) -> Unit)? = null
+
+ private val onColor = resources.getColor(R.color.green)
+ private val offColor = resources.getColor(R.color.red)
+
+ private val knobSize = resources.getDimensionPixelSize(R.dimen.cell_switch_knob_size)
+ private val knobImage = ShapeDrawable(OvalShape()).apply {
+ paint.apply {
+ color = offColor
+ style = Style.FILL
+ }
+
+ intrinsicWidth = knobSize
+ intrinsicHeight = knobSize
+ }
+
+ private val knobView = ImageView(context).apply {
+ setImageDrawable(knobImage)
+ }
+
+ private val knobAnimationDuration = 200L
+ private val knobMaxTranslation =
+ resources.getDimensionPixelOffset(R.dimen.cell_switch_knob_max_translation).toFloat()
+
+ private val knobPosition: Float
+ get() = knobView.translationX / knobMaxTranslation
+
+ private val positionAnimation = ValueAnimator.ofFloat(0f, knobMaxTranslation).apply {
+ addUpdateListener { animation ->
+ knobView.translationX = animation.animatedValue as Float
+ }
+
+ duration = knobAnimationDuration
+ }
+
+ private val colorAnimation = ValueAnimator.ofArgb(offColor, onColor).apply {
+ addUpdateListener { animation ->
+ knobImage.paint.color = animation.animatedValue as Int
+ knobImage.invalidateSelf()
+ }
+
+ duration = knobAnimationDuration
+ }
+
+ private val gestureListener = object : OnGestureListener {
+ private var isScrolling: Boolean = false
+ private var scrollPosition: Float = 0f
+
+ override fun onDown(event: MotionEvent): Boolean {
+ scrollPosition = knobView.translationX
+ return true
+ }
+
+ override fun onFling(
+ downEvent: MotionEvent,
+ upEvent: MotionEvent,
+ velocityX: Float,
+ velocityY: Float
+ ): Boolean {
+ if (velocityX > 0f) {
+ state = State.ON
+ } else if (velocityX < 0f) {
+ state = State.OFF
+ }
+
+ return true
+ }
+
+ override fun onLongPress(event: MotionEvent) {}
+
+ override fun onScroll(
+ downEvent: MotionEvent,
+ moveEvent: MotionEvent,
+ distanceX: Float,
+ distanceY: Float
+ ): Boolean {
+ isScrolling = true
+ scrollPosition -= distanceX
+
+ var fraction = scrollPosition / knobMaxTranslation
+ val playTime = (fraction * knobAnimationDuration).toLong()
+
+ colorAnimation.pause()
+ positionAnimation.pause()
+
+ colorAnimation.currentPlayTime = playTime
+ positionAnimation.currentPlayTime = playTime
+
+ return true
+ }
+
+ override fun onShowPress(event: MotionEvent) {}
+
+ override fun onSingleTapUp(event: MotionEvent): Boolean {
+ when (state) {
+ State.ON -> state = State.OFF
+ State.OFF -> state = State.ON
+ }
+
+ return true
+ }
+
+ fun onUp(): Boolean {
+ if (!isScrolling) {
+ return false
+ }
+
+ if (knobPosition <= 0.5f) {
+ state = State.OFF
+ } else {
+ state = State.ON
+ }
+
+ isScrolling = false
+ scrollPosition = 0f
+
+ return true
+ }
+ }
+
+ private val gestureDetector = GestureDetector(context, gestureListener)
+
+ 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 {
+ setBackground(resources.getDrawable(R.drawable.cell_switch_background, null))
+ addView(knobView, LinearLayout.LayoutParams(knobSize, knobSize).apply {
+ gravity = Gravity.CENTER_VERTICAL
+ leftMargin = resources.getDimensionPixelSize(R.dimen.cell_switch_knob_margin)
+ })
+ }
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ if (gestureDetector.onTouchEvent(event)) {
+ return true
+ } else if (event.actionMasked == MotionEvent.ACTION_UP) {
+ return gestureListener.onUp()
+ }
+
+ return super.onTouchEvent(event)
+ }
+
+ fun forcefullySetState(newState: State) {
+ when (newState) {
+ State.ON -> {
+ knobView.translationX = knobMaxTranslation
+ knobImage.paint.color = onColor
+ }
+ State.OFF -> {
+ knobView.translationX = 0f
+ knobImage.paint.color = offColor
+ }
+ }
+
+ state = newState
+ }
+
+ private fun animateToState() {
+ var playTime = (knobPosition * knobAnimationDuration).toLong()
+
+ when (state) {
+ State.ON -> {
+ colorAnimation.start()
+ positionAnimation.start()
+ }
+ State.OFF -> {
+ colorAnimation.reverse()
+ positionAnimation.reverse()
+
+ playTime = knobAnimationDuration - playTime
+ }
+ }
+
+ colorAnimation.currentPlayTime = playTime
+ positionAnimation.currentPlayTime = playTime
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/PreferencesFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/PreferencesFragment.kt
new file mode 100644
index 0000000000..160caafd22
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/PreferencesFragment.kt
@@ -0,0 +1,65 @@
+package net.mullvad.mullvadvpn.ui
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import net.mullvad.mullvadvpn.R
+
+class PreferencesFragment : ServiceDependentFragment(OnNoService.GoBack) {
+ private lateinit var allowLanToggle: CellSwitch
+
+ private var updateUiJob: Job? = null
+
+ override fun onSafelyCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ val view = inflater.inflate(R.layout.preferences, container, false)
+
+ view.findViewById<View>(R.id.back).setOnClickListener {
+ parentActivity.onBackPressed()
+ }
+
+ allowLanToggle = view.findViewById<CellSwitch>(R.id.allow_lan_toggle).apply {
+ settingsListener.settings?.let { settings ->
+ if (settings.allowLan) {
+ forcefullySetState(CellSwitch.State.ON)
+ } else {
+ forcefullySetState(CellSwitch.State.OFF)
+ }
+ }
+
+ listener = { state ->
+ when (state) {
+ CellSwitch.State.ON -> daemon.setAllowLan(true)
+ CellSwitch.State.OFF -> daemon.setAllowLan(false)
+ }
+ }
+ }
+
+ settingsListener.onAllowLanChange = { allowLan ->
+ updateUiJob?.cancel()
+ updateUiJob = updateUi(allowLan)
+ }
+
+ return view
+ }
+
+ override fun onSafelyDestroyView() {
+ settingsListener.onAllowLanChange = null
+ }
+
+ private fun updateUi(allowLan: Boolean) = GlobalScope.launch(Dispatchers.Main) {
+ if (allowLan) {
+ allowLanToggle.state = CellSwitch.State.ON
+ } else {
+ allowLanToggle.state = CellSwitch.State.OFF
+ }
+ }
+}
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 df68241819..154339b10a 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt
@@ -24,6 +24,7 @@ class SettingsFragment : ServiceAwareFragment() {
private lateinit var appVersionWarning: View
private lateinit var appVersionLabel: TextView
private lateinit var appVersionFooter: View
+ private lateinit var preferencesMenu: View
private lateinit var remainingTimeLabel: RemainingTimeLabel
private lateinit var wireguardKeysMenu: View
@@ -68,6 +69,13 @@ class SettingsFragment : ServiceAwareFragment() {
openSubFragment(AccountFragment())
}
}
+
+ preferencesMenu = view.findViewById<View>(R.id.preferences).apply {
+ setOnClickListener {
+ openSubFragment(PreferencesFragment())
+ }
+ }
+
wireguardKeysMenu = view.findViewById<View>(R.id.wireguard_keys).apply {
setOnClickListener {
openSubFragment(WireguardKeyFragment())
@@ -164,6 +172,7 @@ class SettingsFragment : ServiceAwareFragment() {
}
accountMenu.visibility = visibility
+ preferencesMenu.visibility = visibility
wireguardKeysMenu.visibility = visibility
}
diff --git a/android/src/main/res/drawable/cell_switch_background.xml b/android/src/main/res/drawable/cell_switch_background.xml
new file mode 100644
index 0000000000..71f6fe0802
--- /dev/null
+++ b/android/src/main/res/drawable/cell_switch_background.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle"
+ >
+ <item android:state_enabled="false">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/cell_switch_border_radius"/>
+ <stroke android:color="@color/white20" android:width="2dp"/>
+ <size
+ android:width="@dimen/cell_switch_width"
+ android:height="@dimen/cell_switch_height"
+ />
+ </shape>
+ </item>
+
+ <item android:state_enabled="true">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/cell_switch_border_radius"/>
+ <stroke android:color="@color/white" android:width="2dp"/>
+ <size
+ android:width="@dimen/cell_switch_width"
+ android:height="@dimen/cell_switch_height"
+ />
+ </shape>
+ </item>
+</selector>
diff --git a/android/src/main/res/layout/preferences.xml b/android/src/main/res/layout/preferences.xml
new file mode 100644
index 0000000000..59d42635fe
--- /dev/null
+++ b/android/src/main/res/layout/preferences.xml
@@ -0,0 +1,78 @@
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/darkBlue"
+ android:orientation="vertical"
+ android:gravity="left"
+ android:elevation="2dp"
+ >
+ <LinearLayout android:id="@+id/back"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="12dp"
+ android:orientation="horizontal"
+ android:gravity="center_vertical | left"
+ android:clickable="true"
+ android:background="?android:attr/selectableItemBackground"
+ >
+ <ImageView
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginRight="8dp"
+ android:src="@drawable/icon_back"
+ />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/white60"
+ android:textSize="13sp"
+ android:textStyle="bold"
+ android:text="@string/settings"
+ />
+ </LinearLayout>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:layout_marginLeft="24dp"
+ android:textColor="@color/white"
+ android:textSize="32sp"
+ android:textStyle="bold"
+ android:text="@string/settings_preferences"
+ />
+ <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"
+ />
+</LinearLayout>
diff --git a/android/src/main/res/layout/settings.xml b/android/src/main/res/layout/settings.xml
index 37cf3cb9db..c36c9bb3e8 100644
--- a/android/src/main/res/layout/settings.xml
+++ b/android/src/main/res/layout/settings.xml
@@ -19,7 +19,6 @@
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginLeft="24dp"
- android:layout_marginBottom="24dp"
android:textColor="@color/white"
android:textSize="32sp"
android:textStyle="bold"
@@ -28,7 +27,7 @@
<LinearLayout android:id="@+id/account"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="24dp"
+ android:layout_marginTop="24dp"
android:paddingHorizontal="16dp"
android:background="@drawable/cell_button_background"
android:clickable="true"
@@ -66,10 +65,39 @@
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/wireguard_keys"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="24dp"
+ android:layout_marginTop="24dp"
android:paddingHorizontal="16dp"
android:background="@drawable/cell_button_background"
android:clickable="true"
@@ -98,6 +126,7 @@
<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"
diff --git a/android/src/main/res/values/dimensions.xml b/android/src/main/res/values/dimensions.xml
index 800e1ed0ac..5d106210ea 100644
--- a/android/src/main/res/values/dimensions.xml
+++ b/android/src/main/res/values/dimensions.xml
@@ -6,4 +6,10 @@
<dimen name="account_input_corner_radius">4dp</dimen>
<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_knob_margin">4dp</dimen>
+ <dimen name="cell_switch_knob_max_translation">20dp</dimen>
+ <dimen name="cell_switch_knob_size">24dp</dimen>
</resources>
diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml
index d019f68b17..0a87ff3736 100644
--- a/android/src/main/res/values/strings.xml
+++ b/android/src/main/res/values/strings.xml
@@ -29,6 +29,7 @@
<string name="settings_account">Account</string>
<string name="less_than_a_day_left">less than a day left</string>
<string name="out_of_time">Out of time</string>
+ <string name="settings_preferences">Preferences</string>
<string name="app_version">App version</string>
<string name="update_available_footer">Update available, download to remain safe.</string>
<string name="report_a_problem">Report a problem</string>
@@ -40,6 +41,11 @@
<string name="paid_until">Paid until</string>
<string name="log_out">Log out</string>
+ <string name="local_network_sharing">Local network sharing</string>
+ <string name="allow_lan_footer">
+ Allows access to other devices on the same network for sharing, printing etc.
+ </string>
+
<string name="problem_report_description">
To help you more effectively, your app\'s log file will be attached to this message. Your
data will remain secure and private, as it is anonymised before being sent over an encrypted
diff --git a/mullvad-jni/src/daemon_interface.rs b/mullvad-jni/src/daemon_interface.rs
index fdd03435bf..7071dee363 100644
--- a/mullvad-jni/src/daemon_interface.rs
+++ b/mullvad-jni/src/daemon_interface.rs
@@ -168,6 +168,14 @@ impl DaemonInterface {
rx.wait().map_err(|_| Error::NoResponse)
}
+ pub fn set_allow_lan(&self, allow_lan: bool) -> Result<()> {
+ let (tx, rx) = oneshot::channel();
+
+ self.send_command(ManagementCommand::SetAllowLan(tx, allow_lan))?;
+
+ rx.wait().map_err(|_| Error::NoResponse)
+ }
+
pub fn shutdown(&self) -> Result<()> {
self.send_command(ManagementCommand::Shutdown)
}
diff --git a/mullvad-jni/src/from_java.rs b/mullvad-jni/src/from_java.rs
index 9cccd738d7..3caf3fd523 100644
--- a/mullvad-jni/src/from_java.rs
+++ b/mullvad-jni/src/from_java.rs
@@ -1,6 +1,9 @@
use crate::is_null::IsNull;
use jnix::{
- jni::objects::{JObject, JString},
+ jni::{
+ objects::{JObject, JString},
+ sys::{jboolean, JNI_FALSE},
+ },
JnixEnv,
};
use mullvad_types::relay_constraints::{
@@ -14,6 +17,14 @@ pub trait FromJava<'env> {
fn from_java(env: &JnixEnv<'env>, source: Self::JavaType) -> Self;
}
+impl<'env> FromJava<'env> for bool {
+ type JavaType = jboolean;
+
+ fn from_java(_: &JnixEnv<'env>, source: Self::JavaType) -> Self {
+ source != JNI_FALSE
+ }
+}
+
impl<'env, T> FromJava<'env> for Option<T>
where
T: FromJava<'env>,
diff --git a/mullvad-jni/src/lib.rs b/mullvad-jni/src/lib.rs
index 89528da8c6..7e6dd7f99f 100644
--- a/mullvad-jni/src/lib.rs
+++ b/mullvad-jni/src/lib.rs
@@ -650,6 +650,28 @@ pub extern "system" fn Java_net_mullvad_mullvadvpn_service_MullvadDaemon_setAcco
#[no_mangle]
#[allow(non_snake_case)]
+pub extern "system" fn Java_net_mullvad_mullvadvpn_service_MullvadDaemon_setAllowLan(
+ env: JNIEnv<'_>,
+ _: JObject<'_>,
+ daemon_interface_address: jlong,
+ allow_lan: jboolean,
+) {
+ let env = JnixEnv::from(env);
+
+ if let Some(daemon_interface) = get_daemon_interface(daemon_interface_address) {
+ let allow_lan = bool::from_java(&env, allow_lan);
+
+ if let Err(error) = daemon_interface.set_allow_lan(allow_lan) {
+ log::error!(
+ "{}",
+ error.display_chain_with_msg("Failed to set allow LAN")
+ );
+ }
+ }
+}
+
+#[no_mangle]
+#[allow(non_snake_case)]
pub extern "system" fn Java_net_mullvad_mullvadvpn_service_MullvadDaemon_shutdown(
_: JNIEnv<'_>,
_: JObject<'_>,
diff --git a/mullvad-types/src/settings/mod.rs b/mullvad-types/src/settings/mod.rs
index 89b0fa2ad7..19f350b7f7 100644
--- a/mullvad-types/src/settings/mod.rs
+++ b/mullvad-types/src/settings/mod.rs
@@ -65,7 +65,6 @@ pub struct Settings {
#[cfg_attr(target_os = "android", jnix(skip))]
bridge_state: BridgeState,
/// If the daemon should allow communication with private (LAN) networks.
- #[cfg_attr(target_os = "android", jnix(skip))]
allow_lan: bool,
/// Extra level of kill switch. When this setting is on, the disconnected state will block
/// the firewall to not allow any traffic in or out.