Skip to content

Commit

Permalink
Merge pull request #6096 from k9mail/sign_in_with_google
Browse files Browse the repository at this point in the history
Add "Sign in with Google" button
  • Loading branch information
cketti authored Jun 5, 2022
2 parents 5065afe + d4883d1 commit 3581a02
Show file tree
Hide file tree
Showing 23 changed files with 220 additions and 18 deletions.
4 changes: 4 additions & 0 deletions app/k9mail/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,10 @@
</intent-filter>
</activity>

<activity
android:name=".activity.setup.OAuthFlowActivity"
android:label="@string/account_setup_basics_title" />

<receiver
android:name=".provider.UnreadWidgetProvider"
android:icon="@drawable/ic_launcher"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.fsck.k9.activity.setup

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
Expand Down Expand Up @@ -193,12 +194,19 @@ class AccountSetupBasics : K9Activity() {
connectionSettings.incoming.authenticationType == AuthType.XOAUTH2 &&
connectionSettings.outgoing.authenticationType == AuthType.XOAUTH2
) {
finishAutoSetup(connectionSettings)
startOAuthFlow(connectionSettings)
} else {
startPasswordFlow()
}
}

private fun startOAuthFlow(connectionSettings: ConnectionSettings) {
val account = createAccount(connectionSettings)

val intent = OAuthFlowActivity.buildLaunchIntent(this, account.uuid)
startActivityForResult(intent, REQUEST_CODE_OAUTH)
}

private fun startPasswordFlow() {
uiState = UiState.PASSWORD_FLOW

Expand Down Expand Up @@ -233,6 +241,13 @@ class AccountSetupBasics : K9Activity() {
}

private fun finishAutoSetup(connectionSettings: ConnectionSettings) {
val account = createAccount(connectionSettings)

// Check incoming here. Then check outgoing in onActivityResult()
AccountSetupCheckSettings.actionCheckSettings(this, account, CheckDirection.INCOMING)
}

private fun createAccount(connectionSettings: ConnectionSettings): Account {
val email = emailView.text?.toString() ?: error("Email missing")
val password = passwordView.text?.toString()

Expand All @@ -248,8 +263,7 @@ class AccountSetupBasics : K9Activity() {

localFoldersCreator.createSpecialLocalFolders(account)

// Check incoming here. Then check outgoing in onActivityResult()
AccountSetupCheckSettings.actionCheckSettings(this, account, CheckDirection.INCOMING)
return account
}

private fun onManualSetup() {
Expand Down Expand Up @@ -309,28 +323,39 @@ class AccountSetupBasics : K9Activity() {
}

public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode != AccountSetupCheckSettings.ACTIVITY_REQUEST_CODE) {
super.onActivityResult(requestCode, resultCode, data)
return
when (requestCode) {
REQUEST_CODE_CHECK_SETTINGS -> handleCheckSettingsResult(resultCode)
REQUEST_CODE_OAUTH -> handleSignInResult(resultCode)
else -> super.onActivityResult(requestCode, resultCode, data)
}
}

if (resultCode == RESULT_OK) {
val account = this.account ?: error("Account instance missing")
private fun handleCheckSettingsResult(resultCode: Int) {
if (resultCode != RESULT_OK) return

if (!checkedIncoming) {
// We've successfully checked incoming. Now check outgoing.
checkedIncoming = true
AccountSetupCheckSettings.actionCheckSettings(this, account, CheckDirection.OUTGOING)
} else {
// We've successfully checked outgoing as well.
preferences.saveAccount(account)
Core.setServicesEnabled(applicationContext)
val account = this.account ?: error("Account instance missing")

AccountSetupNames.actionSetNames(this, account)
}
if (!checkedIncoming) {
// We've successfully checked incoming. Now check outgoing.
checkedIncoming = true
AccountSetupCheckSettings.actionCheckSettings(this, account, CheckDirection.OUTGOING)
} else {
// We've successfully checked outgoing as well.
preferences.saveAccount(account)
Core.setServicesEnabled(applicationContext)

AccountSetupNames.actionSetNames(this, account)
}
}

private fun handleSignInResult(resultCode: Int) {
if (resultCode != RESULT_OK) return

val account = this.account ?: error("Account instance missing")

AccountSetupCheckSettings.actionCheckSettings(this, account, CheckDirection.INCOMING)
}

private enum class UiState {
EMAIL_ADDRESS_ONLY,
PASSWORD_FLOW
Expand All @@ -340,6 +365,8 @@ class AccountSetupBasics : K9Activity() {
private const val EXTRA_ACCOUNT = "com.fsck.k9.AccountSetupBasics.account"
private const val STATE_KEY_UI_STATE = "com.fsck.k9.AccountSetupBasics.uiState"
private const val STATE_KEY_CHECKED_INCOMING = "com.fsck.k9.AccountSetupBasics.checkedIncoming"
private const val REQUEST_CODE_CHECK_SETTINGS = AccountSetupCheckSettings.ACTIVITY_REQUEST_CODE
private const val REQUEST_CODE_OAUTH = Activity.RESULT_FIRST_USER + 1

@JvmStatic
fun actionNewAccount(context: Context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ class AuthViewModel(
return authState.isAuthorized
}

fun isUsingGoogle(account: Account): Boolean {
val config = findOAuthConfiguration(account)
return config?.authorizationEndpoint == "https://accounts.google.com/o/oauth2/v2/auth"
}

private fun getOrCreateAuthState(account: Account): AuthState {
return try {
account.oAuthState?.let { AuthState.jsonDeserialize(it) } ?: AuthState()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.fsck.k9.activity.setup

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.core.view.isVisible
import com.fsck.k9.Account
import com.fsck.k9.preferences.AccountManager
import com.fsck.k9.ui.R
import com.fsck.k9.ui.base.K9Activity
import com.fsck.k9.ui.observe
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel

class OAuthFlowActivity : K9Activity() {
private val authViewModel: AuthViewModel by viewModel()
private val accountManager: AccountManager by inject()

private lateinit var errorText: TextView

public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setLayout(R.layout.account_setup_oauth)
setTitle(R.string.account_setup_basics_title)

val accountUUid = intent.getStringExtra(EXTRA_ACCOUNT_UUID) ?: error("Missing account UUID")
val account = accountManager.getAccount(accountUUid) ?: error("Account not found")

errorText = findViewById(R.id.error_text)
val signInButton: View = if (authViewModel.isUsingGoogle(account)) {
findViewById(R.id.google_sign_in_button)
} else {
findViewById(R.id.oauth_sign_in_button)
}

signInButton.isVisible = true
signInButton.setOnClickListener { startOAuthFlow(account) }

authViewModel.init(activityResultRegistry, lifecycle)

authViewModel.uiState.observe(this) { state ->
handleUiUpdates(state)
}
}

private fun handleUiUpdates(state: AuthFlowState) {
when (state) {
AuthFlowState.Idle -> {
return
}
AuthFlowState.Success -> {
setResult(RESULT_OK)
finish()
}
AuthFlowState.Canceled -> {
errorText.text = getString(R.string.account_setup_failed_dlg_oauth_flow_canceled)
}
is AuthFlowState.Failed -> {
errorText.text = getString(R.string.account_setup_failed_dlg_oauth_flow_failed, state)
}
AuthFlowState.NotSupported -> {
errorText.text = getString(R.string.account_setup_failed_dlg_oauth_not_supported)
}
AuthFlowState.BrowserNotFound -> {
errorText.text = getString(R.string.account_setup_failed_dlg_browser_not_found)
}
}

authViewModel.authResultConsumed()
}

private fun startOAuthFlow(account: Account) {
errorText.text = ""

authViewModel.login(account)
}

companion object {
private const val EXTRA_ACCOUNT_UUID = "accountUuid"

fun buildLaunchIntent(context: Context, accountUuid: String): Intent {
return Intent(context, OAuthFlowActivity::class.java).apply {
putExtra(EXTRA_ACCOUNT_UUID, accountUuid)
}
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:drawable="@drawable/btn_google_signin_dark_disabled" />
<item android:state_pressed="true" android:drawable="@drawable/btn_google_signin_dark_pressed" />
<item android:state_focused="true" android:drawable="@drawable/btn_google_signin_dark_focus" />
<item android:drawable="@drawable/btn_google_signin_dark_normal" />
</selector>
64 changes: 64 additions & 0 deletions app/ui/legacy/src/main/res/layout/account_setup_oauth.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.fsck.k9.activity.setup.OAuthFlowActivity">

<include layout="@layout/toolbar" />

<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:fadingEdge="none"
android:padding="16dp"
android:scrollbarStyle="outsideInset">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_horizontal"
android:orientation="vertical">

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:text="@string/account_setup_oauth_description"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />

<Button
android:id="@+id/oauth_sign_in_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/account_setup_oauth_sign_in"
android:visibility="gone" />

<Button
android:id="@+id/google_sign_in_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/btn_google_signin_dark"
android:text="@string/account_setup_oauth_sign_in_with_google"
android:textAllCaps="false"
android:textColor="#ffffff"
android:textSize="14sp"
android:visibility="gone"
tools:visibility="visible" />

<TextView
android:id="@+id/error_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
tools:text="@string/account_setup_failed_dlg_browser_not_found" />

</LinearLayout>

</ScrollView>

</LinearLayout>
6 changes: 6 additions & 0 deletions app/ui/legacy/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,12 @@ Please submit bug reports, contribute new features and ask questions at
<string name="account_setup_basics_email_hint">Email address</string>
<string name="account_setup_basics_password_hint">Password</string>

<string name="account_setup_oauth_description">To use this email account with K-9 Mail, you need to sign in and grant the app access to your emails.</string>
<!-- Displayed below the 'account_setup_oauth_description' text -->
<string name="account_setup_oauth_sign_in">Sign in</string>
<!-- Displayed below the 'account_setup_oauth_description' text -->
<string name="account_setup_oauth_sign_in_with_google">Sign in with Google</string>

<!-- Please use the same translation for "screen lock" as is used in the 'Security' section in Android's settings app -->
<string name="account_setup_basics_show_password_need_lock">To view your password here, enable screen lock on this device.</string>
<string name="account_setup_basics_show_password_biometrics_title">Verify your identity</string>
Expand Down

0 comments on commit 3581a02

Please sign in to comment.