summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2019-03-13 12:54:59 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2019-03-13 12:54:59 -0300
commitea895f9835ed9dc1b46cb251f43a6f75a1b41f8b (patch)
treec492e8a6f03b3d98166b679f6c9f41abe5b6c951
parent50d3d18d4710949179e19372bdcd7cbe93fbb260 (diff)
parent2fc8e2b5af1c77bd3a9af300bf321f4312b03ca8 (diff)
downloadmullvadvpn-ea895f9835ed9dc1b46cb251f43a6f75a1b41f8b.tar.xz
mullvadvpn-ea895f9835ed9dc1b46cb251f43a6f75a1b41f8b.zip
Merge branch 'android-login-screen'
-rw-r--r--android/build.gradle1
-rw-r--r--android/src/main/AndroidManifest.xml1
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/AccountInput.kt110
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/LoginFragment.kt77
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/LoginState.kt8
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt11
-rw-r--r--android/src/main/res/drawable-hdpi/logo_icon.pngbin0 -> 5637 bytes
-rw-r--r--android/src/main/res/drawable-mdpi/logo_icon.pngbin0 -> 3360 bytes
-rw-r--r--android/src/main/res/drawable-xhdpi/logo_icon.pngbin0 -> 7686 bytes
-rw-r--r--android/src/main/res/drawable-xxhdpi/logo_icon.pngbin0 -> 12600 bytes
-rw-r--r--android/src/main/res/drawable-xxxhdpi/logo_icon.pngbin0 -> 17155 bytes
-rw-r--r--android/src/main/res/drawable/icon_arrow_blue20.xml14
-rw-r--r--android/src/main/res/drawable/icon_arrow_white.xml14
-rw-r--r--android/src/main/res/drawable/icon_fail.xml17
-rw-r--r--android/src/main/res/drawable/icon_spinner.xml24
-rw-r--r--android/src/main/res/drawable/icon_success.xml17
-rw-r--r--android/src/main/res/drawable/login_button_arrow.xml8
-rw-r--r--android/src/main/res/drawable/login_button_background.xml17
-rw-r--r--android/src/main/res/drawable/text_input_cursor.xml5
-rw-r--r--android/src/main/res/layout/login.xml134
-rw-r--r--android/src/main/res/values/colors.xml10
-rw-r--r--android/src/main/res/values/strings.xml9
22 files changed, 477 insertions, 0 deletions
diff --git a/android/build.gradle b/android/build.gradle
index 2559316d8e..43bfdb53c9 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -31,6 +31,7 @@ repositories {
dependencies {
implementation 'com.android.support:appcompat-v7:28.0.0'
+ implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.3.21'
}
buildscript {
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
index 2836f3d63a..450c316618 100644
--- a/android/src/main/AndroidManifest.xml
+++ b/android/src/main/AndroidManifest.xml
@@ -15,6 +15,7 @@
<activity
android:name=".MainActivity"
android:label="@string/app_name"
+ android:windowSoftInputMode="adjustPan"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/AccountInput.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/AccountInput.kt
new file mode 100644
index 0000000000..246ed57e11
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/AccountInput.kt
@@ -0,0 +1,110 @@
+package net.mullvad.mullvadvpn
+
+import android.content.Context
+import android.view.View
+import android.text.Editable
+import android.text.TextWatcher
+import android.widget.EditText
+import android.widget.ImageButton
+
+const val MIN_ACCOUNT_TOKEN_LENGTH = 10
+
+class AccountInput(val parentView: View, val context: Context) {
+ private val disabledBackgroundColor = context.getColor(R.color.white20)
+ private val disabledTextColor = context.getColor(R.color.white)
+ private val enabledBackgroundColor = context.getColor(R.color.white)
+ private val enabledTextColor = context.getColor(R.color.blue)
+ private val errorTextColor = context.getColor(R.color.red)
+
+ private var usingErrorColor = false
+
+ var state = LoginState.Initial
+ set(value) {
+ when (value) {
+ LoginState.Initial -> initialState()
+ LoginState.InProgress -> loggingInState()
+ LoginState.Success -> successState()
+ LoginState.Failure -> failureState()
+ }
+ }
+
+ val input: EditText = parentView.findViewById(R.id.account_input)
+ val button: ImageButton = parentView.findViewById(R.id.login_button)
+
+ var onLogin: ((String) -> Unit)? = null
+
+ init {
+ input.addTextChangedListener(InputWatcher())
+ button.setOnClickListener { onLogin?.invoke(input.text.toString()) }
+ setButtonEnabled(false)
+ }
+
+ private fun initialState() {
+ setButtonEnabled(input.text.length >= MIN_ACCOUNT_TOKEN_LENGTH)
+ button.visibility = View.VISIBLE
+
+ input.apply {
+ setBackgroundColor(enabledBackgroundColor)
+ setTextColor(enabledTextColor)
+ setEnabled(true)
+ visibility = View.VISIBLE
+ }
+ }
+
+ private fun loggingInState() {
+ setButtonEnabled(false)
+ button.visibility = View.GONE
+
+ input.apply {
+ setBackgroundColor(disabledBackgroundColor)
+ setTextColor(disabledTextColor)
+ setEnabled(false)
+ visibility = View.VISIBLE
+ }
+ }
+
+ private fun successState() {
+ setButtonEnabled(false)
+ button.visibility = View.GONE
+ input.visibility = View.GONE
+ }
+
+ private fun failureState() {
+ setButtonEnabled(false)
+ button.visibility = View.VISIBLE
+
+ input.apply {
+ setBackgroundColor(enabledBackgroundColor)
+ setTextColor(errorTextColor)
+ setEnabled(true)
+ visibility = View.VISIBLE
+ }
+
+ usingErrorColor = true
+ }
+
+ private fun setButtonEnabled(enabled: Boolean) {
+ button.apply {
+ if (enabled != isEnabled()) {
+ setEnabled(enabled)
+ setClickable(enabled)
+ setFocusable(enabled)
+ }
+ }
+ }
+
+ inner class InputWatcher : TextWatcher {
+ override fun beforeTextChanged(text: CharSequence, start: Int, count: Int, after: Int) {}
+
+ override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) {}
+
+ override fun afterTextChanged(text: Editable) {
+ setButtonEnabled(text.length >= MIN_ACCOUNT_TOKEN_LENGTH)
+
+ if (usingErrorColor) {
+ input.setTextColor(enabledTextColor)
+ usingErrorColor = false
+ }
+ }
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/LoginFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/LoginFragment.kt
new file mode 100644
index 0000000000..d5a06a9d75
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/LoginFragment.kt
@@ -0,0 +1,77 @@
+package net.mullvad.mullvadvpn
+
+import android.os.Bundle
+import android.os.Handler
+import android.support.v4.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+
+class LoginFragment : Fragment() {
+ private lateinit var title: TextView
+ private lateinit var subtitle: TextView
+ private lateinit var loggingInStatus: View
+ private lateinit var loggedInStatus: View
+ private lateinit var loginFailStatus: View
+ private lateinit var accountInput: AccountInput
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ val view = inflater.inflate(R.layout.login, container, false)
+
+ title = view.findViewById(R.id.title)
+ subtitle = view.findViewById(R.id.subtitle)
+ loggingInStatus = view.findViewById(R.id.logging_in_status)
+ loggedInStatus = view.findViewById(R.id.logged_in_status)
+ loginFailStatus = view.findViewById(R.id.login_fail_status)
+
+ accountInput = AccountInput(view, context!!)
+ accountInput.onLogin = { accountToken -> login(accountToken) }
+
+ return view
+ }
+
+ private fun login(accountToken: String) {
+ title.setText(R.string.logging_in_title)
+ subtitle.setText(R.string.logging_in_description)
+
+ loggingInStatus.visibility = View.VISIBLE
+ loginFailStatus.visibility = View.GONE
+ loggedInStatus.visibility = View.GONE
+
+ accountInput.state = LoginState.InProgress
+
+ // TODO: Actually log in
+ if ("1234567890".equals(accountToken)) {
+ Handler().postDelayed(Runnable { loggedIn() }, 1000)
+ } else {
+ Handler().postDelayed(Runnable { loginFailure() }, 1000)
+ }
+ }
+
+ private fun loggedIn() {
+ title.setText(R.string.logged_in_title)
+ subtitle.setText("")
+
+ loggingInStatus.visibility = View.GONE
+ loginFailStatus.visibility = View.GONE
+ loggedInStatus.visibility = View.VISIBLE
+
+ accountInput.state = LoginState.Success
+ }
+
+ private fun loginFailure() {
+ title.setText(R.string.login_fail_title)
+ subtitle.setText(R.string.login_fail_description)
+
+ loggingInStatus.visibility = View.GONE
+ loginFailStatus.visibility = View.VISIBLE
+ loggedInStatus.visibility = View.GONE
+
+ accountInput.state = LoginState.Failure
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/LoginState.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/LoginState.kt
new file mode 100644
index 0000000000..df8cc94642
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/LoginState.kt
@@ -0,0 +1,8 @@
+package net.mullvad.mullvadvpn
+
+enum class LoginState {
+ Initial,
+ InProgress,
+ Success,
+ Failure,
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt
index b05f4df3b1..b5fc9c3306 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt
@@ -7,5 +7,16 @@ class MainActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main)
+
+ if (savedInstanceState == null) {
+ addInitialFragment()
+ }
+ }
+
+ private fun addInitialFragment() {
+ supportFragmentManager?.beginTransaction()?.apply {
+ add(R.id.main_fragment, LoginFragment())
+ commit()
+ }
}
}
diff --git a/android/src/main/res/drawable-hdpi/logo_icon.png b/android/src/main/res/drawable-hdpi/logo_icon.png
new file mode 100644
index 0000000000..1701158506
--- /dev/null
+++ b/android/src/main/res/drawable-hdpi/logo_icon.png
Binary files differ
diff --git a/android/src/main/res/drawable-mdpi/logo_icon.png b/android/src/main/res/drawable-mdpi/logo_icon.png
new file mode 100644
index 0000000000..4d7b2af4e0
--- /dev/null
+++ b/android/src/main/res/drawable-mdpi/logo_icon.png
Binary files differ
diff --git a/android/src/main/res/drawable-xhdpi/logo_icon.png b/android/src/main/res/drawable-xhdpi/logo_icon.png
new file mode 100644
index 0000000000..975ba02b3c
--- /dev/null
+++ b/android/src/main/res/drawable-xhdpi/logo_icon.png
Binary files differ
diff --git a/android/src/main/res/drawable-xxhdpi/logo_icon.png b/android/src/main/res/drawable-xxhdpi/logo_icon.png
new file mode 100644
index 0000000000..5a27e27631
--- /dev/null
+++ b/android/src/main/res/drawable-xxhdpi/logo_icon.png
Binary files differ
diff --git a/android/src/main/res/drawable-xxxhdpi/logo_icon.png b/android/src/main/res/drawable-xxxhdpi/logo_icon.png
new file mode 100644
index 0000000000..5476db715c
--- /dev/null
+++ b/android/src/main/res/drawable-xxxhdpi/logo_icon.png
Binary files differ
diff --git a/android/src/main/res/drawable/icon_arrow_blue20.xml b/android/src/main/res/drawable/icon_arrow_blue20.xml
new file mode 100644
index 0000000000..d6a4616deb
--- /dev/null
+++ b/android/src/main/res/drawable/icon_arrow_blue20.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="16dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="16.0"
+ >
+ <group>
+ <path android:fillColor="#33294D73"
+ android:pathData="M18.7015867,9 L14.4331381,12.762659 C13.851665,13.2752305 13.8579999,14.1003943 14.4392669,14.612784 C15.0245863,15.1287461 15.9602099,15.1275926 16.5380921,14.6181865 L23.5668627,8.42228969 C23.8565791,8.16690324 24.000373,7.83391619 23.999837,7.50067932 L24,7.4966702 C23.999589,7.16348359 23.8547954,6.83138119 23.5668627,6.57756713 L16.5380921,0.381670278 C15.956619,-0.130901228 15.0205338,-0.125317014 14.4392669,0.387072772 C13.8539474,0.903034846 13.8552559,1.72779176 14.4331381,2.23719784 L18.7017491,6 L1.50909424,6 C0.66354084,6 0,6.67157288 0,7.5 C0,8.33420277 0.675644504,9 1.50909424,9 L18.7015867,9 Z"
+ />
+ </group>
+</vector>
diff --git a/android/src/main/res/drawable/icon_arrow_white.xml b/android/src/main/res/drawable/icon_arrow_white.xml
new file mode 100644
index 0000000000..b8497d5fe6
--- /dev/null
+++ b/android/src/main/res/drawable/icon_arrow_white.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="16dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="16.0"
+ >
+ <group>
+ <path android:fillColor="#FFFFFF"
+ android:pathData="M18.7015867,9 L14.4331381,12.762659 C13.851665,13.2752305 13.8579999,14.1003943 14.4392669,14.612784 C15.0245863,15.1287461 15.9602099,15.1275926 16.5380921,14.6181865 L23.5668627,8.42228969 C23.8565791,8.16690324 24.000373,7.83391619 23.999837,7.50067932 L24,7.4966702 C23.999589,7.16348359 23.8547954,6.83138119 23.5668627,6.57756713 L16.5380921,0.381670278 C15.956619,-0.130901228 15.0205338,-0.125317014 14.4392669,0.387072772 C13.8539474,0.903034846 13.8552559,1.72779176 14.4331381,2.23719784 L18.7017491,6 L1.50909424,6 C0.66354084,6 0,6.67157288 0,7.5 C0,8.33420277 0.675644504,9 1.50909424,9 L18.7015867,9 Z"
+ />
+ </group>
+</vector>
diff --git a/android/src/main/res/drawable/icon_fail.xml b/android/src/main/res/drawable/icon_fail.xml
new file mode 100644
index 0000000000..61d9ad79d8
--- /dev/null
+++ b/android/src/main/res/drawable/icon_fail.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="60dp"
+ android:height="60dp"
+ android:viewportWidth="60.0"
+ android:viewportHeight="60.0"
+ >
+ <group>
+ <path android:fillColor="#FFFFFF"
+ android:pathData="M8 30 a 22,22 0 1,0 44,0 a 22,22 0 1,0 -44,0 Z"
+ />
+ <path android:fillColor="#D0021B"
+ android:pathData="M33.2371523,30 L41.337119,21.9033278 C42.2203329,21.020473 42.223948,19.5681264 41.3300331,18.6745751 C40.429886,17.774794 38.9899682,17.7778525 38.0999667,18.6674921 L30,26.7641643 L21.9000333,18.6674921 C21.0100318,17.7778525 19.570114,17.774794 18.6699669,18.6745751 C17.776052,19.5681264 17.7796671,21.020473 18.662881,21.9033278 L26.7628477,30 L18.662881,38.0966722 C17.7796671,38.979527 17.776052,40.4318736 18.6699669,41.3254249 C19.570114,42.225206 21.0100318,42.2221475 21.9000333,41.3325079 L30,33.2358357 L38.0999667,41.3325079 C38.9899682,42.2221475 40.429886,42.225206 41.3300331,41.3254249 C42.223948,40.4318736 42.2203329,38.979527 41.337119,38.0966722 L33.2371523,30 Z"
+ />
+ </group>
+</vector>
diff --git a/android/src/main/res/drawable/icon_spinner.xml b/android/src/main/res/drawable/icon_spinner.xml
new file mode 100644
index 0000000000..040ea6a864
--- /dev/null
+++ b/android/src/main/res/drawable/icon_spinner.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rotate
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromDegrees="0"
+ android:toDegrees="360"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ >
+ <vector
+ android:width="60dp"
+ android:height="60dp"
+ android:viewportWidth="60.0"
+ android:viewportHeight="60.0"
+ >
+ <group>
+ <path android:fillColor="#33FFFFFF"
+ android:pathData="M27.6038221,6.11991768 C40.7924274,4.79654517 52.5567098,14.4152168 53.8800823,27.6038221 C55.2034548,40.7924274 45.5847832,52.5567098 32.3961779,53.8800823 C19.2075726,55.2034548 7.4432902,45.5847832 6.11991768,32.3961779 C4.79654517,19.2075726 14.4152168,7.4432902 27.6038221,6.11991768 Z M28.4025481,14.0799451 C19.6101445,14.9621935 13.1976968,22.8050484 14.0799451,31.5974519 C14.9621935,40.3898555 22.8050484,46.8023032 31.5974519,45.9200549 C40.3898555,45.0378065 46.8023032,37.1949516 45.9200549,28.4025481 C45.0378065,19.6101445 37.1949516,13.1976968 28.4025481,14.0799451 Z"
+ />
+ <path android:fillColor="#FFFFFF"
+ android:pathData="M25.2028561,6.48431564 C12.2155023,9.13370504 3.83492624,21.80979 6.48431564,34.7971439 C9.13370504,47.7844977 21.80979,56.1650738 34.7971439,53.5156844 C44.2988591,51.577357 51.5941458,44.163762 53.514681,34.8276709 C53.9598043,32.6638409 52.5665172,30.5488664 50.4026872,30.1037431 C48.2388572,29.6586198 46.1238826,31.0519068 45.6787593,33.2157369 C44.3979534,39.441981 39.5342463,44.3845633 33.1980959,45.6771229 C24.53986,47.4433825 16.0891367,41.8563318 14.3228771,33.1980959 C12.5566175,24.53986 18.1436682,16.0891367 26.8019041,14.3228771 C28.9664631,13.8813122 30.3632257,11.7686314 29.9216608,9.60407239 C29.4800959,7.43951342 27.3674151,6.04275074 25.2028561,6.48431564 Z"
+ />
+ </group>
+ </vector>
+</rotate>
diff --git a/android/src/main/res/drawable/icon_success.xml b/android/src/main/res/drawable/icon_success.xml
new file mode 100644
index 0000000000..a4ad910d5e
--- /dev/null
+++ b/android/src/main/res/drawable/icon_success.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="60dp"
+ android:height="60dp"
+ android:viewportWidth="60.0"
+ android:viewportHeight="60.0"
+ >
+ <group>
+ <path android:fillColor="#FFFFFF"
+ android:pathData="M8 30 a 22,22 0 1,0 44,0 a 22,22 0 1,0 -44,0 Z"
+ />
+ <path android:fillColor="#44AD4D"
+ android:pathData="M19.4142136,28.5857864 C18.633165,27.8047379 17.366835,27.8047379 16.5857864,28.5857864 C15.8047379,29.366835 15.8047379,30.633165 16.5857864,31.4142136 L24.5857864,39.4142136 C25.366835,40.1952621 26.633165,40.1952621 27.4142136,39.4142136 L43.4142136,23.4142136 C44.1952621,22.633165 44.1952621,21.366835 43.4142136,20.5857864 C42.633165,19.8047379 41.366835,19.8047379 40.5857864,20.5857864 L26,35.1715729 L19.4142136,28.5857864 Z"
+ />
+ </group>
+</vector>
diff --git a/android/src/main/res/drawable/login_button_arrow.xml b/android/src/main/res/drawable/login_button_arrow.xml
new file mode 100644
index 0000000000..4a51e51585
--- /dev/null
+++ b/android/src/main/res/drawable/login_button_arrow.xml
@@ -0,0 +1,8 @@
+<?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" android:drawable="@drawable/icon_arrow_blue20"/>
+ <item android:state_enabled="true" android:drawable="@drawable/icon_arrow_white"/>
+</selector>
diff --git a/android/src/main/res/drawable/login_button_background.xml b/android/src/main/res/drawable/login_button_background.xml
new file mode 100644
index 0000000000..8622392653
--- /dev/null
+++ b/android/src/main/res/drawable/login_button_background.xml
@@ -0,0 +1,17 @@
+<?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>
+ <solid android:color="@color/white"/>
+ </shape>
+ </item>
+
+ <item android:state_enabled="true">
+ <shape>
+ <solid android:color="@color/green"/>
+ </shape>
+ </item>
+</selector>
diff --git a/android/src/main/res/drawable/text_input_cursor.xml b/android/src/main/res/drawable/text_input_cursor.xml
new file mode 100644
index 0000000000..ffa98a486b
--- /dev/null
+++ b/android/src/main/res/drawable/text_input_cursor.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/darkBlue"/>
+ <size android:width="2sp" android:height="24sp"/>
+</shape>
diff --git a/android/src/main/res/layout/login.xml b/android/src/main/res/layout/login.xml
new file mode 100644
index 0000000000..58e5b12e02
--- /dev/null
+++ b/android/src/main/res/layout/login.xml
@@ -0,0 +1,134 @@
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/main_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:padding="12dp"
+ >
+ <ImageView
+ android:layout_width="49dp"
+ android:layout_height="50dp"
+ android:src="@drawable/logo_icon"
+ />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="8dp"
+ android:textColor="@color/white60"
+ android:textSize="24sp"
+ android:textStyle="bold"
+ android:text="@string/app_name"
+ android:textAllCaps="true"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:padding="24dp"
+ >
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ />
+
+ <FrameLayout
+ android:layout_width="60dp"
+ android:layout_height="60dp"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="30dp"
+ >
+ <ProgressBar android:id="@+id/logging_in_status"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:indeterminate="true"
+ android:indeterminateOnly="true"
+ android:indeterminateDuration="600"
+ android:indeterminateDrawable="@drawable/icon_spinner"
+ android:visibility="gone"
+ />
+ <ImageView android:id="@+id/logged_in_status"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:src="@drawable/icon_success"
+ android:visibility="gone"
+ />
+ <ImageView android:id="@+id/login_fail_status"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:src="@drawable/icon_fail"
+ android:visibility="gone"
+ />
+ </FrameLayout>
+
+ <TextView android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:layout_marginBottom="4dp"
+ android:gravity="start"
+ android:textColor="@color/white"
+ android:textSize="32sp"
+ android:textStyle="bold"
+ android:text="@string/login_title"
+ />
+ <TextView android:id="@+id/subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:layout_marginBottom="10dp"
+ android:gravity="start"
+ android:textColor="@color/white60"
+ android:textSize="13sp"
+ android:text="@string/login_description"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:orientation="horizontal"
+ >
+ <EditText
+ android:id="@+id/account_input"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:paddingHorizontal="12dp"
+ android:background="@color/white"
+ android:inputType="number"
+ android:singleLine="true"
+ android:imeOptions="flagNoPersonalizedLearning"
+ android:textCursorDrawable="@drawable/text_input_cursor"
+ android:hint="@string/login_hint"
+ android:textColorHint="@color/blue40"
+ android:textColor="@color/blue"
+ android:textSize="20sp"
+ android:textStyle="bold"
+ />
+ <ImageButton android:id="@+id/login_button"
+ android:layout_width="48dp"
+ android:layout_height="match_parent"
+ android:layout_weight="0"
+ android:background="@drawable/login_button_background"
+ android:src="@drawable/login_button_arrow"
+ />
+ </LinearLayout>
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="3"
+ />
+ </LinearLayout>
+</LinearLayout>
diff --git a/android/src/main/res/values/colors.xml b/android/src/main/res/values/colors.xml
index b0bf7bdb56..22a7e4108d 100644
--- a/android/src/main/res/values/colors.xml
+++ b/android/src/main/res/values/colors.xml
@@ -1,4 +1,14 @@
<resources>
<color name="colorPrimary">#294D73</color>
<color name="blue">#294D73</color>
+ <color name="blue40">#66294D73</color>
+ <color name="blue20">#33294D73</color>
+ <color name="darkBlue">#192E45</color>
+ <color name="white">#FFFFFF</color>
+ <color name="white60">#99FFFFFF</color>
+ <color name="white20">#33FFFFFF</color>
+ <color name="green">#44AD4D</color>
+ <color name="red">#D0021B</color>
+
+ <color name="textInputBorder">#234161</color>
</resources>
diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml
index e37acca63b..59c207b71c 100644
--- a/android/src/main/res/values/strings.xml
+++ b/android/src/main/res/values/strings.xml
@@ -1,3 +1,12 @@
<resources>
<string name="app_name">Mullvad VPN</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>
+ <string name="logging_in_title">Logging in</string>
+ <string name="logging_in_description">Checking account number</string>
+ <string name="logged_in_title">Login successful</string>
+ <string name="login_fail_title">Login failed</string>
+ <string name="login_fail_description">Invalid account number, try again</string>
</resources>