summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/LaunchFragment.kt70
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/LoginFragment.kt1
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt2
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt3
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/model/Settings.kt4
-rw-r--r--android/src/main/res/drawable-hdpi/launch_logo.pngbin0 -> 15280 bytes
-rw-r--r--android/src/main/res/drawable-mdpi/launch_logo.pngbin0 -> 9451 bytes
-rw-r--r--android/src/main/res/drawable-xhdpi/launch_logo.pngbin0 -> 21077 bytes
-rw-r--r--android/src/main/res/drawable-xxhdpi/launch_logo.pngbin0 -> 32823 bytes
-rw-r--r--android/src/main/res/drawable-xxxhdpi/launch_logo.pngbin0 -> 45026 bytes
-rw-r--r--android/src/main/res/layout/launch.xml37
-rw-r--r--android/src/main/res/values/strings.xml2
-rw-r--r--mullvad-jni/src/daemon_interface.rs18
-rw-r--r--mullvad-jni/src/from_java.rs22
-rw-r--r--mullvad-jni/src/into_java.rs30
-rw-r--r--mullvad-jni/src/lib.rs38
16 files changed, 222 insertions, 5 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/LaunchFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/LaunchFragment.kt
new file mode 100644
index 0000000000..043c8efe15
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/LaunchFragment.kt
@@ -0,0 +1,70 @@
+package net.mullvad.mullvadvpn
+
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.Job
+
+import android.content.Context
+import android.os.Bundle
+import android.support.v4.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+
+class LaunchFragment : Fragment() {
+ private lateinit var accountTokenCheckJob: Deferred<Boolean>
+ private lateinit var advanceToNextScreenJob: Job
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+ accountTokenCheckJob = checkForAccountToken()
+ advanceToNextScreenJob = advanceToNextScreen()
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ) = inflater.inflate(R.layout.launch, container, false)
+
+ override fun onDestroy() {
+ accountTokenCheckJob.cancel()
+ advanceToNextScreenJob.cancel()
+ super.onDestroy()
+ }
+
+ private fun checkForAccountToken() = GlobalScope.async(Dispatchers.Default) {
+ val parentActivity = activity as MainActivity
+ val daemon = parentActivity.asyncDaemon.await()
+ val settings = daemon.getSettings()
+
+ settings.accountToken != null
+ }
+
+ private fun advanceToNextScreen() = GlobalScope.launch(Dispatchers.Main) {
+ val accountTokenIsSet = accountTokenCheckJob.await()
+
+ if (accountTokenIsSet) {
+ advanceToConnectScreen()
+ } else {
+ advanceToLoginScreen()
+ }
+ }
+
+ private fun advanceToLoginScreen() {
+ fragmentManager?.beginTransaction()?.apply {
+ replace(R.id.main_fragment, LoginFragment())
+ commit()
+ }
+ }
+
+ private fun advanceToConnectScreen() {
+ fragmentManager?.beginTransaction()?.apply {
+ replace(R.id.main_fragment, ConnectFragment())
+ commit()
+ }
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/LoginFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/LoginFragment.kt
index 3ead35333c..dd5fd1a41c 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/LoginFragment.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/LoginFragment.kt
@@ -65,6 +65,7 @@ class LoginFragment : Fragment() {
val accountData = daemon.getAccountData(accountToken)
if (accountData != null) {
+ daemon.setAccount(accountToken)
true
} else {
false
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt
index b751bf63d9..f9eb6d9a29 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt
@@ -38,7 +38,7 @@ class MainActivity : FragmentActivity() {
private fun addInitialFragment() {
supportFragmentManager?.beginTransaction()?.apply {
- add(R.id.main_fragment, LoginFragment())
+ add(R.id.main_fragment, LaunchFragment())
commit()
}
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt
index c7025ea644..152eaac970 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt
@@ -1,6 +1,7 @@
package net.mullvad.mullvadvpn
import net.mullvad.mullvadvpn.model.AccountData
+import net.mullvad.mullvadvpn.model.Settings
class MullvadDaemon {
init {
@@ -9,6 +10,8 @@ class MullvadDaemon {
}
external fun getAccountData(accountToken: String): AccountData?
+ external fun getSettings(): Settings
+ external fun setAccount(accountToken: String?)
private external fun initialize()
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/Settings.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/Settings.kt
new file mode 100644
index 0000000000..1254492b57
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/Settings.kt
@@ -0,0 +1,4 @@
+package net.mullvad.mullvadvpn.model
+
+data class Settings(var accountToken: String?) {
+}
diff --git a/android/src/main/res/drawable-hdpi/launch_logo.png b/android/src/main/res/drawable-hdpi/launch_logo.png
new file mode 100644
index 0000000000..542ba7986f
--- /dev/null
+++ b/android/src/main/res/drawable-hdpi/launch_logo.png
Binary files differ
diff --git a/android/src/main/res/drawable-mdpi/launch_logo.png b/android/src/main/res/drawable-mdpi/launch_logo.png
new file mode 100644
index 0000000000..2ee89ae828
--- /dev/null
+++ b/android/src/main/res/drawable-mdpi/launch_logo.png
Binary files differ
diff --git a/android/src/main/res/drawable-xhdpi/launch_logo.png b/android/src/main/res/drawable-xhdpi/launch_logo.png
new file mode 100644
index 0000000000..c68c053832
--- /dev/null
+++ b/android/src/main/res/drawable-xhdpi/launch_logo.png
Binary files differ
diff --git a/android/src/main/res/drawable-xxhdpi/launch_logo.png b/android/src/main/res/drawable-xxhdpi/launch_logo.png
new file mode 100644
index 0000000000..1604009282
--- /dev/null
+++ b/android/src/main/res/drawable-xxhdpi/launch_logo.png
Binary files differ
diff --git a/android/src/main/res/drawable-xxxhdpi/launch_logo.png b/android/src/main/res/drawable-xxxhdpi/launch_logo.png
new file mode 100644
index 0000000000..8a72edd366
--- /dev/null
+++ b/android/src/main/res/drawable-xxxhdpi/launch_logo.png
Binary files differ
diff --git a/android/src/main/res/layout/launch.xml b/android/src/main/res/layout/launch.xml
new file mode 100644
index 0000000000..d8cf918727
--- /dev/null
+++ b/android/src/main/res/layout/launch.xml
@@ -0,0 +1,37 @@
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="vertical"
+ android:gravity="center"
+ >
+ <ImageView
+ android:layout_width="120dp"
+ android:layout_height="120dp"
+ android:layout_marginBottom="4dp"
+ android:src="@drawable/launch_logo"
+ />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:textColor="@color/white60"
+ android:textSize="24sp"
+ android:textStyle="bold"
+ android:text="@string/app_name"
+ android:textAllCaps="true"
+ />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/white40"
+ android:textSize="14sp"
+ android:text="@string/connecting_to_daemon"
+ />
+ </LinearLayout>
+</FrameLayout>
diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml
index 24f8c9867e..4a3d46b2c8 100644
--- a/android/src/main/res/values/strings.xml
+++ b/android/src/main/res/values/strings.xml
@@ -1,6 +1,8 @@
<resources>
<string name="app_name">Mullvad VPN</string>
+ <string name="connecting_to_daemon">Connecting to daemon...</string>
+
<string name="login_title">Login</string>
<string name="login_description">Enter your account number</string>
<string name="login_hint">0000 0000 0000 0000</string>
diff --git a/mullvad-jni/src/daemon_interface.rs b/mullvad-jni/src/daemon_interface.rs
index 6b0e907eda..21dabea9aa 100644
--- a/mullvad-jni/src/daemon_interface.rs
+++ b/mullvad-jni/src/daemon_interface.rs
@@ -1,6 +1,6 @@
use futures::{sync::oneshot, Future};
use mullvad_daemon::{DaemonCommandSender, ManagementCommand};
-use mullvad_types::account::AccountData;
+use mullvad_types::{account::AccountData, settings::Settings};
#[derive(Debug, err_derive::Error)]
pub enum Error {
@@ -45,6 +45,22 @@ impl DaemonInterface {
.map_err(Error::RpcError)
}
+ pub fn get_settings(&self) -> Result<Settings> {
+ let (tx, rx) = oneshot::channel();
+
+ self.send_command(ManagementCommand::GetSettings(tx))?;
+
+ Ok(rx.wait().map_err(|_| Error::NoResponse)?)
+ }
+
+ pub fn set_account(&self, account_token: Option<String>) -> Result<()> {
+ let (tx, rx) = oneshot::channel();
+
+ self.send_command(ManagementCommand::SetAccount(tx, account_token))?;
+
+ rx.wait().map_err(|_| Error::NoResponse)
+ }
+
fn send_command(&self, command: ManagementCommand) -> Result<()> {
let sender = self.command_sender.as_ref().ok_or(Error::NoSender)?;
diff --git a/mullvad-jni/src/from_java.rs b/mullvad-jni/src/from_java.rs
index 526a83e1fb..11b98ad753 100644
--- a/mullvad-jni/src/from_java.rs
+++ b/mullvad-jni/src/from_java.rs
@@ -1,4 +1,8 @@
-use jni::{objects::JString, JNIEnv};
+use jni::{
+ objects::{JObject, JString},
+ JNIEnv,
+};
+use std::ops::Deref;
pub trait FromJava<'env> {
type JavaType: 'env;
@@ -6,6 +10,22 @@ pub trait FromJava<'env> {
fn from_java(env: &JNIEnv<'env>, source: Self::JavaType) -> Self;
}
+impl<'env, T> FromJava<'env> for Option<T>
+where
+ T: FromJava<'env>,
+ T::JavaType: Deref<Target = JObject<'env>>,
+{
+ type JavaType = T::JavaType;
+
+ fn from_java(env: &JNIEnv<'env>, source: Self::JavaType) -> Self {
+ if source.is_null() {
+ None
+ } else {
+ Some(T::from_java(env, source))
+ }
+ }
+}
+
impl<'env> FromJava<'env> for String {
type JavaType = JString<'env>;
diff --git a/mullvad-jni/src/into_java.rs b/mullvad-jni/src/into_java.rs
index 952338ccb7..4f8fc3f287 100644
--- a/mullvad-jni/src/into_java.rs
+++ b/mullvad-jni/src/into_java.rs
@@ -3,7 +3,7 @@ use jni::{
objects::{JObject, JString, JValue},
JNIEnv,
};
-use mullvad_types::account::AccountData;
+use mullvad_types::{account::AccountData, settings::Settings};
pub trait IntoJava<'env> {
type JavaType;
@@ -11,6 +11,21 @@ pub trait IntoJava<'env> {
fn into_java(self, env: &JNIEnv<'env>) -> Self::JavaType;
}
+impl<'env, T> IntoJava<'env> for Option<T>
+where
+ T: IntoJava<'env>,
+ T::JavaType: From<JObject<'env>>,
+{
+ type JavaType = T::JavaType;
+
+ fn into_java(self, env: &JNIEnv<'env>) -> Self::JavaType {
+ match self {
+ Some(data) => data.into_java(env),
+ None => T::JavaType::from(JObject::null()),
+ }
+ }
+}
+
impl<'env> IntoJava<'env> for String {
type JavaType = JString<'env>;
@@ -31,3 +46,16 @@ impl<'env> IntoJava<'env> for AccountData {
.expect("Failed to create AccountData Java object")
}
}
+
+impl<'env> IntoJava<'env> for Settings {
+ type JavaType = JObject<'env>;
+
+ fn into_java(self, env: &JNIEnv<'env>) -> Self::JavaType {
+ let class = get_class("net/mullvad/mullvadvpn/model/Settings");
+ let account_token = env.auto_local(JObject::from(self.get_account_token().into_java(env)));
+ let parameters = [JValue::Object(account_token.as_obj())];
+
+ env.new_object(&class, "(Ljava/lang/String;)V", &parameters)
+ .expect("Failed to create Settings Java object")
+ }
+}
diff --git a/mullvad-jni/src/lib.rs b/mullvad-jni/src/lib.rs
index 35d3825dcf..6195b56b52 100644
--- a/mullvad-jni/src/lib.rs
+++ b/mullvad-jni/src/lib.rs
@@ -18,7 +18,10 @@ use talpid_types::{tunnel::TunnelStateTransition, ErrorExt};
const LOG_FILENAME: &str = "daemon.log";
-const CLASSES_TO_LOAD: &[&str] = &["net/mullvad/mullvadvpn/model/AccountData"];
+const CLASSES_TO_LOAD: &[&str] = &[
+ "net/mullvad/mullvadvpn/model/AccountData",
+ "net/mullvad/mullvadvpn/model/Settings",
+];
lazy_static! {
static ref DAEMON_INTERFACE: Mutex<DaemonInterface> = Mutex::new(DaemonInterface::new());
@@ -162,3 +165,36 @@ pub extern "system" fn Java_net_mullvad_mullvadvpn_MullvadDaemon_getAccountData<
}
}
}
+
+#[no_mangle]
+#[allow(non_snake_case)]
+pub extern "system" fn Java_net_mullvad_mullvadvpn_MullvadDaemon_getSettings<'env, 'this>(
+ env: JNIEnv<'env>,
+ _: JObject<'this>,
+) -> JObject<'env> {
+ let daemon = DAEMON_INTERFACE.lock();
+
+ match daemon.get_settings() {
+ Ok(settings) => settings.into_java(&env),
+ Err(error) => {
+ log::error!("{}", error.display_chain_with_msg("Failed to get settings"));
+ JObject::null()
+ }
+ }
+}
+
+#[no_mangle]
+#[allow(non_snake_case)]
+pub extern "system" fn Java_net_mullvad_mullvadvpn_MullvadDaemon_setAccount(
+ env: JNIEnv,
+ _: JObject,
+ accountToken: JString,
+) {
+ let daemon = DAEMON_INTERFACE.lock();
+
+ let account = <Option<String> as FromJava>::from_java(&env, accountToken);
+
+ if let Err(error) = daemon.set_account(account) {
+ log::error!("{}", error.display_chain_with_msg("Failed to set account"));
+ }
+}