diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-05-01 14:25:25 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-05-01 14:25:25 -0300 |
| commit | 990624960bbd7a70e1195f2a5eab9f9d6f2a3944 (patch) | |
| tree | c6046b1112b02bd55755f5eecca3cba4ff435479 /android/src | |
| parent | bdae0324fbd0e968f43f910d8bdb1f0634aaa40f (diff) | |
| parent | c7220af262c492ff15cac8b8cc09215a3c0874b6 (diff) | |
| download | mullvadvpn-990624960bbd7a70e1195f2a5eab9f9d6f2a3944.tar.xz mullvadvpn-990624960bbd7a70e1195f2a5eab9f9d6f2a3944.zip | |
Merge branch 'new-account-welcome-screen'
Diffstat (limited to 'android/src')
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 |
