Merge pull request #6096 from k9mail/sign_in_with_google
Add "Sign in with Google" button
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 464 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 331 B |
After Width: | Height: | Size: 811 B |
After Width: | Height: | Size: 758 B |
After Width: | Height: | Size: 768 B |
After Width: | Height: | Size: 622 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 949 B |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.5 KiB |
|
@ -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
app/ui/legacy/src/main/res/layout/account_setup_oauth.xml
Normal file
|
@ -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>
|
|
@ -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>
|
||||
|
|