summaryrefslogtreecommitdiffhomepage
path: root/android
diff options
context:
space:
mode:
Diffstat (limited to 'android')
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/LoginFragment.kt26
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/WelcomeFragment.kt130
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Button.kt1
-rw-r--r--android/src/main/res/layout/welcome.xml90
-rw-r--r--android/src/main/res/values/strings.xml6
5 files changed, 244 insertions, 9 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/LoginFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/LoginFragment.kt
index c8663eb149..959431b85a 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/LoginFragment.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/LoginFragment.kt
@@ -1,6 +1,7 @@
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
@@ -16,6 +17,11 @@ import net.mullvad.mullvadvpn.ui.widget.Button
import net.mullvad.mullvadvpn.util.JobTracker
class LoginFragment : ServiceDependentFragment(OnNoService.GoToLaunchScreen) {
+ enum class LoginResult {
+ ExistingAccount,
+ NewAccount;
+ }
+
private lateinit var title: TextView
private lateinit var subtitle: TextView
private lateinit var loggingInStatus: View
@@ -24,7 +30,7 @@ class LoginFragment : ServiceDependentFragment(OnNoService.GoToLaunchScreen) {
private lateinit var accountInput: AccountInput
private val jobTracker = JobTracker()
- private val loggedIn = CompletableDeferred<Unit>()
+ private val loggedIn = CompletableDeferred<LoginResult>()
override fun onSafelyCreateView(
inflater: LayoutInflater,
@@ -54,8 +60,10 @@ class LoginFragment : ServiceDependentFragment(OnNoService.GoToLaunchScreen) {
override fun onSafelyResume() {
jobTracker.newUiJob("advanceToNextScreen") {
- loggedIn.join()
- openConnectScreen()
+ when (loggedIn.await()) {
+ LoginResult.ExistingAccount -> openNextScreen(ConnectFragment())
+ LoginResult.NewAccount -> openNextScreen(WelcomeFragment())
+ }
}
fetchHistory()
@@ -86,7 +94,7 @@ class LoginFragment : ServiceDependentFragment(OnNoService.GoToLaunchScreen) {
if (accountToken == null) {
loginFailure(R.string.failed_to_create_account)
} else {
- loggedIn(resources.getString(R.string.account_created))
+ loggedIn(resources.getString(R.string.account_created), LoginResult.NewAccount)
}
}
@@ -126,17 +134,17 @@ class LoginFragment : ServiceDependentFragment(OnNoService.GoToLaunchScreen) {
}
if (loginSucceeded) {
- loggedIn("")
+ loggedIn("", LoginResult.ExistingAccount)
} else {
loginFailure(R.string.login_fail_description)
}
}
}
- private suspend fun loggedIn(subtitleMessage: String) {
+ private suspend fun loggedIn(subtitleMessage: String, result: LoginResult) {
showLoggedInMessage(subtitleMessage)
delay(1000)
- loggedIn.complete(Unit)
+ loggedIn.complete(result)
}
private fun showLoggedInMessage(subtitleMessage: String) {
@@ -150,9 +158,9 @@ class LoginFragment : ServiceDependentFragment(OnNoService.GoToLaunchScreen) {
accountInput.state = LoginState.Success
}
- private fun openConnectScreen() {
+ private fun openNextScreen(fragment: Fragment) {
fragmentManager?.beginTransaction()?.apply {
- replace(R.id.main_fragment, ConnectFragment())
+ replace(R.id.main_fragment, fragment)
commit()
}
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/WelcomeFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/WelcomeFragment.kt
new file mode 100644
index 0000000000..fd2a0c9c96
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/WelcomeFragment.kt
@@ -0,0 +1,130 @@
+package net.mullvad.mullvadvpn.ui
+
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import android.widget.Toast
+import kotlinx.coroutines.delay
+import net.mullvad.mullvadvpn.R
+import net.mullvad.mullvadvpn.ui.widget.UrlButton
+import net.mullvad.mullvadvpn.util.JobTracker
+import org.joda.time.DateTime
+
+val POLL_INTERVAL: Long = 15 /* s */ * 1000 /* ms */
+
+class WelcomeFragment : ServiceDependentFragment(OnNoService.GoToLaunchScreen) {
+ private val jobTracker = JobTracker()
+
+ private lateinit var accountLabel: TextView
+
+ override fun onSafelyCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ val view = inflater.inflate(R.layout.welcome, container, false)
+
+ view.findViewById<View>(R.id.settings).setOnClickListener {
+ parentActivity.openSettings()
+ }
+
+ accountLabel = view.findViewById<TextView>(R.id.account_number).apply {
+ setOnClickListener { copyAccountTokenToClipboard() }
+ }
+
+ view.findViewById<UrlButton>(R.id.buy_credit).apply {
+ prepare(daemon, jobTracker)
+ }
+
+ return view
+ }
+
+ override fun onSafelyResume() {
+ accountCache.onAccountDataChange = { account, expiry ->
+ updateAccountNumber(account)
+ checkExpiry(expiry)
+ }
+
+ jobTracker.newBackgroundJob("pollAccountData") {
+ while (true) {
+ accountCache.refetch()
+ delay(POLL_INTERVAL)
+ }
+ }
+ }
+
+ override fun onSafelyPause() {
+ accountCache.onAccountDataChange = null
+ jobTracker.cancelJob("pollAccountData")
+ }
+
+ override fun onSafelyDestroyView() {
+ jobTracker.cancelAllJobs()
+ }
+
+ private fun updateAccountNumber(rawAccountNumber: String?) {
+ val accountText = rawAccountNumber?.let { account ->
+ addSpacesToAccountText(account)
+ }
+
+ jobTracker.newUiJob("updateAccountNumber") {
+ accountLabel.text = accountText ?: ""
+ accountLabel.setEnabled(accountText != null && accountText.length > 0)
+ }
+ }
+
+ private fun addSpacesToAccountText(account: String): String {
+ val length = account.length
+
+ if (length == 0) {
+ return ""
+ } else {
+ val numParts = (length - 1) / 4 + 1
+
+ val parts = Array(numParts) { index ->
+ val startIndex = index * 4
+ val endIndex = minOf(startIndex + 4, length)
+
+ account.substring(startIndex, endIndex)
+ }
+
+ return parts.joinToString(" ")
+ }
+ }
+
+ private fun checkExpiry(maybeExpiry: DateTime?) {
+ maybeExpiry?.let { expiry ->
+ if (expiry.isAfterNow()) {
+ jobTracker.newUiJob("advanceToConnectScreen") {
+ advanceToConnectScreen()
+ }
+ }
+ }
+ }
+
+ private fun advanceToConnectScreen() {
+ fragmentManager?.beginTransaction()?.apply {
+ replace(R.id.main_fragment, ConnectFragment())
+ commit()
+ }
+ }
+
+ private fun copyAccountTokenToClipboard() {
+ val accountToken = accountLabel.text
+ val clipboardLabel = resources.getString(R.string.mullvad_account_number)
+ val toastMessage = resources.getString(R.string.copied_mullvad_account_number)
+
+ val context = parentActivity
+ val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
+ val clipData = ClipData.newPlainText(clipboardLabel, accountToken)
+
+ clipboard.primaryClip = clipData
+
+ Toast.makeText(context, toastMessage, Toast.LENGTH_SHORT).show()
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Button.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Button.kt
index 826c93e1e4..106be5b28a 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Button.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Button.kt
@@ -92,6 +92,7 @@ open class Button : FrameLayout {
override fun setEnabled(enabled: Boolean) {
super.setEnabled(enabled)
+ button.setEnabled(enabled)
if (enabled) {
alpha = 1.0f
diff --git a/android/src/main/res/layout/welcome.xml b/android/src/main/res/layout/welcome.xml
new file mode 100644
index 0000000000..521bfc4dff
--- /dev/null
+++ b/android/src/main/res/layout/welcome.xml
@@ -0,0 +1,90 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:mullvad="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/main_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <LinearLayout android:id="@+id/header_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:background="@color/red"
+ android:elevation="0.5dp">
+ <ImageView android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:layout_marginLeft="12dp"
+ android:layout_marginVertical="12dp"
+ android:layout_weight="0"
+ android:src="@drawable/logo_icon" />
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="8dp"
+ android:layout_marginVertical="12dp"
+ android:layout_weight="1"
+ android:textColor="@color/white80"
+ android:textSize="24sp"
+ android:textStyle="bold"
+ android:text="@string/app_name"
+ android:textAllCaps="true" />
+ <ImageButton android:id="@+id/settings"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="0"
+ android:paddingHorizontal="12dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:src="@drawable/icon_settings" />
+ </LinearLayout>
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="24dp"
+ android:textColor="@color/white"
+ android:textSize="32sp"
+ android:textStyle="bold"
+ android:text="@string/congrats" />
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="11dp"
+ android:textColor="@color/white"
+ android:textSize="13sp"
+ android:text="@string/here_is_your_account_number" />
+ <TextView android:id="@+id/account_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingHorizontal="24dp"
+ android:paddingVertical="11dp"
+ android:clickable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:textColor="@color/white"
+ android:textSize="24sp"
+ android:textStyle="bold"
+ android:text="" />
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="11dp"
+ android:textColor="@color/white"
+ android:textSize="13sp"
+ android:text="@string/pay_to_start_using" />
+ <Space android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:orientation="vertical"
+ android:padding="24dp"
+ android:background="@color/darkBlue">
+ <net.mullvad.mullvadvpn.ui.widget.UrlButton android:id="@+id/buy_credit"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ mullvad:buttonColor="green"
+ mullvad:text="@string/buy_credit"
+ mullvad:url="@string/account_url"
+ mullvad:withToken="true" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml
index ff70bf0730..a2d06c83cc 100644
--- a/android/src/main/res/values/strings.xml
+++ b/android/src/main/res/values/strings.xml
@@ -32,6 +32,12 @@
<string name="creating_new_account">Creating new account</string>
<string name="failed_to_create_account">Failed to create account</string>
<string name="account_created">Account created</string>
+ <string name="congrats">Congrats!</string>
+ <string name="here_is_your_account_number">Here\'s your account number.
+ Save it!</string>
+ <string name="pay_to_start_using">To start using the app, you first need
+ to add time to you account. Buy credit on our website.</string>
+ <string name="buy_credit">Buy credit</string>
<string name="settings">Settings</string>
<string name="settings_account">Account</string>
<string name="less_than_a_day_left">less than a day