package com.fsck.k9.activity.setup; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.text.method.DigitsKeyListener; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.*; import android.widget.CompoundButton.OnCheckedChangeListener; import com.fsck.k9.*; import com.fsck.k9.activity.K9Activity; import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.AuthType; import com.fsck.k9.mail.ConnectionSecurity; import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.transport.SmtpTransport; import com.fsck.k9.net.ssl.SslHelper; import com.fsck.k9.view.ClientCertificateSpinner; import com.fsck.k9.view.ClientCertificateSpinner.OnClientCertificateChangedListener; import java.net.URI; import java.net.URISyntaxException; public class AccountSetupOutgoing extends K9Activity implements OnClickListener, OnCheckedChangeListener { private static final String EXTRA_ACCOUNT = "account"; private static final String EXTRA_MAKE_DEFAULT = "makeDefault"; private static final String SMTP_PORT = "587"; private static final String SMTP_SSL_PORT = "465"; private EditText mUsernameView; private EditText mPasswordView; private ClientCertificateSpinner mClientCertificateSpinner; private TextView mClientCertificateLabelView; private TextView mPasswordLabelView; private EditText mServerView; private EditText mPortView; private CheckBox mRequireLoginView; private ViewGroup mRequireLoginSettingsView; private Spinner mSecurityTypeView; private Spinner mAuthTypeView; private ArrayAdapter mAuthTypeAdapter; private Button mNextButton; private Account mAccount; private boolean mMakeDefault; public static void actionOutgoingSettings(Context context, Account account, boolean makeDefault) { Intent i = new Intent(context, AccountSetupOutgoing.class); i.putExtra(EXTRA_ACCOUNT, account.getUuid()); i.putExtra(EXTRA_MAKE_DEFAULT, makeDefault); context.startActivity(i); } public static void actionEditOutgoingSettings(Context context, Account account) { context.startActivity(intentActionEditOutgoingSettings(context, account)); } public static Intent intentActionEditOutgoingSettings(Context context, Account account) { Intent i = new Intent(context, AccountSetupOutgoing.class); i.setAction(Intent.ACTION_EDIT); i.putExtra(EXTRA_ACCOUNT, account.getUuid()); return i; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.account_setup_outgoing); String accountUuid = getIntent().getStringExtra(EXTRA_ACCOUNT); mAccount = Preferences.getPreferences(this).getAccount(accountUuid); try { if (new URI(mAccount.getStoreUri()).getScheme().startsWith("webdav")) { mAccount.setTransportUri(mAccount.getStoreUri()); AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.OUTGOING); } } catch (URISyntaxException e) { // TODO Auto-generated catch block e.printStackTrace(); } mUsernameView = (EditText)findViewById(R.id.account_username); mPasswordView = (EditText)findViewById(R.id.account_password); mClientCertificateSpinner = (ClientCertificateSpinner)findViewById(R.id.account_client_certificate_spinner); mClientCertificateLabelView = (TextView)findViewById(R.id.account_client_certificate_label); mPasswordLabelView = (TextView)findViewById(R.id.account_password_label); mServerView = (EditText)findViewById(R.id.account_server); mPortView = (EditText)findViewById(R.id.account_port); mRequireLoginView = (CheckBox)findViewById(R.id.account_require_login); mRequireLoginSettingsView = (ViewGroup)findViewById(R.id.account_require_login_settings); mSecurityTypeView = (Spinner)findViewById(R.id.account_security_type); mAuthTypeView = (Spinner)findViewById(R.id.account_auth_type); mNextButton = (Button)findViewById(R.id.next); mNextButton.setOnClickListener(this); mRequireLoginView.setOnCheckedChangeListener(this); mSecurityTypeView.setAdapter(ConnectionSecurity.getArrayAdapter(this)); mAuthTypeAdapter = AuthType.getArrayAdapter(this, SslHelper.isClientCertificateSupportAvailable()); mAuthTypeView.setAdapter(mAuthTypeAdapter); mUsernameView.addTextChangedListener(validationTextWatcher); mPasswordView.addTextChangedListener(validationTextWatcher); mServerView.addTextChangedListener(validationTextWatcher); mPortView.addTextChangedListener(validationTextWatcher); mClientCertificateSpinner.setOnClientCertificateChangedListener(clientCertificateChangedListener); mAuthTypeView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { updateViewFromAuthType(); } @Override public void onNothingSelected(AdapterView parent) { /* unused */ } }); /* * Only allow digits in the port field. */ mPortView.setKeyListener(DigitsKeyListener.getInstance("0123456789")); //FIXME: get Account object again? accountUuid = getIntent().getStringExtra(EXTRA_ACCOUNT); mAccount = Preferences.getPreferences(this).getAccount(accountUuid); mMakeDefault = getIntent().getBooleanExtra(EXTRA_MAKE_DEFAULT, false); /* * If we're being reloaded we override the original account with the one * we saved */ if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_ACCOUNT)) { accountUuid = savedInstanceState.getString(EXTRA_ACCOUNT); mAccount = Preferences.getPreferences(this).getAccount(accountUuid); } try { ServerSettings settings = Transport.decodeTransportUri(mAccount.getTransportUri()); updateAuthPlainTextFromSecurityType(settings.connectionSecurity); // The first item is selected if settings.authenticationType is null or is not in mAuthTypeAdapter int position = mAuthTypeAdapter.getPosition(settings.authenticationType); mAuthTypeView.setSelection(position, false); // Select currently configured security type mSecurityTypeView.setSelection(settings.connectionSecurity.ordinal(), false); if (settings.username != null) { mUsernameView.setText(settings.username); mRequireLoginView.setChecked(true); } if (settings.password != null) { mPasswordView.setText(settings.password); } if (settings.clientCertificateAlias != null) { mClientCertificateSpinner.setAlias(settings.clientCertificateAlias); } /* * Updates the port when the user changes the security type. This allows * us to show a reasonable default which the user can change. * * Note: It's important that we set the listener *after* an initial option has been * selected by the code above. Otherwise the listener might be called after * onCreate() has been processed and the current port value set later in this * method is overridden with the default port for the selected security type. */ mSecurityTypeView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { // this indirectly triggers validateFields because the port text is watched updatePortFromSecurityType(); } @Override public void onNothingSelected(AdapterView parent) { /* unused */ } }); if (settings.host != null) { mServerView.setText(settings.host); } if (settings.port != -1) { mPortView.setText(Integer.toString(settings.port)); } else { updatePortFromSecurityType(); } validateFields(); } catch (Exception e) { /* * We should always be able to parse our own settings. */ failure(e); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString(EXTRA_ACCOUNT, mAccount.getUuid()); } /** * Shows/hides password field and client certificate spinner */ private void updateViewFromAuthType() { AuthType authType = (AuthType) mAuthTypeView.getSelectedItem(); boolean isAuthTypeExternal = AuthType.EXTERNAL.equals(authType); if (isAuthTypeExternal) { // hide password fields, show client certificate fields mPasswordView.setVisibility(View.GONE); mPasswordLabelView.setVisibility(View.GONE); mClientCertificateLabelView.setVisibility(View.VISIBLE); mClientCertificateSpinner.setVisibility(View.VISIBLE); } else { // show password fields, hide client certificate fields mPasswordView.setVisibility(View.VISIBLE); mPasswordLabelView.setVisibility(View.VISIBLE); mClientCertificateLabelView.setVisibility(View.GONE); mClientCertificateSpinner.setVisibility(View.GONE); } validateFields(); } private void validateFields() { AuthType authType = (AuthType) mAuthTypeView.getSelectedItem(); boolean isAuthTypeExternal = AuthType.EXTERNAL.equals(authType); ConnectionSecurity connectionSecurity = (ConnectionSecurity) mSecurityTypeView.getSelectedItem(); boolean hasConnectionSecurity = (!connectionSecurity.equals(ConnectionSecurity.NONE)); boolean hasValidCertificateAlias = mClientCertificateSpinner.getAlias() != null; boolean hasValidUserName = Utility.requiredFieldValid(mUsernameView); boolean hasValidPasswordSettings = hasValidUserName && !isAuthTypeExternal && Utility.requiredFieldValid(mPasswordView); boolean hasValidExternalAuthSettings = hasValidUserName && isAuthTypeExternal && hasConnectionSecurity && hasValidCertificateAlias; mNextButton .setEnabled(Utility.domainFieldValid(mServerView) && Utility.requiredFieldValid(mPortView) && (!mRequireLoginView.isChecked() || hasValidPasswordSettings || hasValidExternalAuthSettings)); Utility.setCompoundDrawablesAlpha(mNextButton, mNextButton.isEnabled() ? 255 : 128); } private void updatePortFromSecurityType() { ConnectionSecurity securityType = (ConnectionSecurity) mSecurityTypeView.getSelectedItem(); mPortView.setText(getDefaultSmtpPort(securityType)); updateAuthPlainTextFromSecurityType(securityType); } private String getDefaultSmtpPort(ConnectionSecurity securityType) { String port; switch (securityType) { case NONE: case STARTTLS_REQUIRED: port = SMTP_PORT; break; case SSL_TLS_REQUIRED: port = SMTP_SSL_PORT; break; default: port = ""; Log.e(K9.LOG_TAG, "Unhandled ConnectionSecurity type encountered"); } return port; } private void updateAuthPlainTextFromSecurityType(ConnectionSecurity securityType) { switch (securityType) { case NONE: AuthType.PLAIN.useInsecureText(true, mAuthTypeAdapter); break; default: AuthType.PLAIN.useInsecureText(false, mAuthTypeAdapter); } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK) { if (Intent.ACTION_EDIT.equals(getIntent().getAction())) { mAccount.save(Preferences.getPreferences(this)); finish(); } else { AccountSetupOptions.actionOptions(this, mAccount, mMakeDefault); finish(); } } } protected void onNext() { ConnectionSecurity securityType = (ConnectionSecurity) mSecurityTypeView.getSelectedItem(); String uri; String username = null; String password = null; String clientCertificateAlias = null; AuthType authType = null; if (mRequireLoginView.isChecked()) { username = mUsernameView.getText().toString(); authType = (AuthType) mAuthTypeView.getSelectedItem(); if (AuthType.EXTERNAL.equals(authType)) { clientCertificateAlias = mClientCertificateSpinner.getAlias(); } else { password = mPasswordView.getText().toString(); } } String newHost = mServerView.getText().toString(); int newPort = Integer.parseInt(mPortView.getText().toString()); String type = SmtpTransport.TRANSPORT_TYPE; ServerSettings server = new ServerSettings(type, newHost, newPort, securityType, authType, username, password, clientCertificateAlias); uri = Transport.createTransportUri(server); mAccount.deleteCertificate(newHost, newPort, CheckDirection.OUTGOING); mAccount.setTransportUri(uri); AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.OUTGOING); } public void onClick(View v) { switch (v.getId()) { case R.id.next: onNext(); break; } } public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mRequireLoginSettingsView.setVisibility(isChecked ? View.VISIBLE : View.GONE); validateFields(); } private void failure(Exception use) { Log.e(K9.LOG_TAG, "Failure", use); String toastText = getString(R.string.account_setup_bad_uri, use.getMessage()); Toast toast = Toast.makeText(getApplication(), toastText, Toast.LENGTH_LONG); toast.show(); } /* * Calls validateFields() which enables or disables the Next button * based on the fields' validity. */ TextWatcher validationTextWatcher = new TextWatcher() { public void afterTextChanged(Editable s) { validateFields(); } public void beforeTextChanged(CharSequence s, int start, int count, int after) { } public void onTextChanged(CharSequence s, int start, int before, int count) { } }; OnClientCertificateChangedListener clientCertificateChangedListener = new OnClientCertificateChangedListener() { @Override public void onClientCertificateChanged(String alias) { validateFields(); } }; }