Defragment AccountSetupBasics
* This was an early attempt at fragmentation
* But it turned out that no fragment was needed here - it is
not shared or reused in any way.
* Precursor to doing final layouts for the activity.
* Tested working:
Automatic provider lookup
Manual
POP/IMAP and EAS flow modes (from accounts & sync)
Force account creation intent
Provider note
Duplicate account detection
Change-Id: I434edd4b152391d36e70440873932db3ec1a1c99
diff --git a/res/layout-xlarge-land/account_setup_basics.xml b/res/layout-xlarge-land/account_setup_basics.xml
index 70402bc..e26b503 100644
--- a/res/layout-xlarge-land/account_setup_basics.xml
+++ b/res/layout-xlarge-land/account_setup_basics.xml
@@ -60,15 +60,17 @@
style="@style/accountSetupButton"
android:text="@string/next_action" />
- <!-- Fragment on the left containing the setup info -->
- <fragment
- android:id="@+id/setup_basics_fragment"
- class="com.android.email.activity.setup.AccountSetupBasicsFragment"
+ <!-- Frame on the left containing the (common) setup info -->
+ <FrameLayout
android:layout_below="@+id/top_divider"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/manual_setup"
android:layout_marginRight="64dip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- />
+ >
+ <include
+ layout="@layout/account_setup_basics_common"
+ />
+ </FrameLayout>
</RelativeLayout>
diff --git a/res/layout-xlarge-port/account_setup_basics.xml b/res/layout-xlarge-port/account_setup_basics.xml
index 6e85b06..b83bf24 100644
--- a/res/layout-xlarge-port/account_setup_basics.xml
+++ b/res/layout-xlarge-port/account_setup_basics.xml
@@ -42,28 +42,31 @@
android:layout_height="1px"
android:background="@color/account_setup_divider_color" />
- <!-- Fragment in the middle containing the setup info -->
- <fragment
- android:id="@+id/setup_basics_fragment"
- class="com.android.email.activity.setup.AccountSetupBasicsFragment"
+ <!-- Frame on the left containing the (common) setup info -->
+ <FrameLayout
+ android:id="@+id/common"
android:layout_below="@+id/top_divider"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- />
+ >
+ <include
+ layout="@layout/account_setup_basics_common"
+ />
+ </FrameLayout>
<!-- Buttons below -->
<Button
android:id="@+id/manual_setup"
- android:layout_below="@+id/setup_basics_fragment"
+ android:layout_below="@+id/common"
android:layout_marginTop="48dip"
android:layout_alignParentLeft="true"
style="@style/accountSetupButton"
android:text="@string/account_setup_basics_manual_setup_action" />
<Button
android:id="@+id/next"
- android:layout_below="@+id/setup_basics_fragment"
+ android:layout_below="@+id/common"
android:layout_marginTop="48dip"
android:layout_alignParentRight="true"
style="@style/accountSetupButton"
diff --git a/res/layout/account_setup_basics_fragment.xml b/res/layout-xlarge/account_setup_basics_common.xml
similarity index 98%
rename from res/layout/account_setup_basics_fragment.xml
rename to res/layout-xlarge/account_setup_basics_common.xml
index 5a537f7..b58262e 100644
--- a/res/layout/account_setup_basics_fragment.xml
+++ b/res/layout-xlarge/account_setup_basics_common.xml
@@ -15,6 +15,7 @@
-->
<!-- Common data-entry area of initial account setup screen - email, password, default check -->
+<!-- xlarge version -->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
diff --git a/res/layout/account_setup_basics.xml b/res/layout/account_setup_basics.xml
index 473a78d..dc9eb80 100644
--- a/res/layout/account_setup_basics.xml
+++ b/res/layout/account_setup_basics.xml
@@ -25,29 +25,33 @@
android:layout_height="match_parent"
>
- <!-- Fragment in the middle containing the setup info -->
- <fragment
- android:id="@+id/setup_basics_fragment"
- class="com.android.email.activity.setup.AccountSetupBasicsFragment"
+ <!-- Frame on the left containing the (common) setup info -->
+ <!-- TODO need phone-sized UX here -->
+ <FrameLayout
+ android:id="@+id/common"
android:layout_below="@+id/top_divider"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
- />
+ >
+ <include
+ layout="@layout/account_setup_basics_common"
+ />
+ </FrameLayout>
<!-- Buttons below -->
<Button
android:id="@+id/manual_setup"
- android:layout_below="@+id/setup_basics_fragment"
+ android:layout_below="@+id/common"
android:layout_alignParentLeft="true"
android:layout_marginTop="16dip"
style="@style/accountSetupButton"
android:text="@string/account_setup_basics_manual_setup_action" />
<Button
android:id="@+id/next"
- android:layout_below="@+id/setup_basics_fragment"
+ android:layout_below="@+id/common"
android:layout_alignParentRight="true"
android:layout_marginTop="16dip"
style="@style/accountSetupButton"
diff --git a/src/com/android/email/activity/setup/AccountSetupBasics.java b/src/com/android/email/activity/setup/AccountSetupBasics.java
index dcbbd7a..ca604b3 100644
--- a/src/com/android/email/activity/setup/AccountSetupBasics.java
+++ b/src/com/android/email/activity/setup/AccountSetupBasics.java
@@ -17,11 +17,14 @@
package com.android.email.activity.setup;
import com.android.email.Email;
+import com.android.email.EmailAddressValidator;
import com.android.email.R;
import com.android.email.Utility;
import com.android.email.VendorPolicyLoader;
import com.android.email.activity.ActivityHelper;
import com.android.email.activity.Welcome;
+import com.android.email.activity.setup.AccountSettingsUtils.Provider;
+import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.HostAuth;
@@ -29,15 +32,29 @@
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
import android.app.FragmentTransaction;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
+import android.os.AsyncTask;
import android.os.Bundle;
+import android.text.Editable;
import android.text.TextUtils;
+import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.net.URI;
+import java.net.URISyntaxException;
/**
* Prompts the user for the email address and password. Also prompts for "Use this account as
@@ -67,8 +84,9 @@
* Note: Exchange accounts that require device security policies cannot be created automatically.
*/
public class AccountSetupBasics extends AccountSetupActivity
- implements AccountSetupBasicsFragment.Callback, AccountCheckSettingsFragment.Callbacks,
- OnClickListener {
+ implements OnClickListener, TextWatcher, AccountCheckSettingsFragment.Callbacks {
+
+ private final static boolean ENTER_DEBUG_SCREEN = true;
/**
* Direct access for forcing account creation
@@ -81,9 +99,18 @@
private final String EXTRA_CREATE_ACCOUNT_OUTGOING = "OUTGOING";
private final Boolean DEBUG_ALLOW_NON_MONKEY_CREATION = true; // STOPSHIP - must be FALSE
- private AccountSetupBasicsFragment mFragment;
- private boolean mManualButtonDisplayed;
- private boolean mNextButtonEnabled;
+ private final static String STATE_KEY_PROVIDER = "AccountSetupBasics.provider";
+
+ // NOTE: If you change this value, confirm that the new interval exists in arrays.xml
+ private final static int DEFAULT_ACCOUNT_CHECK_INTERVAL = 15;
+
+ // Support for UI
+ private TextView mWelcomeView;
+ private EditText mEmailView;
+ private EditText mPasswordView;
+ private CheckBox mDefaultView;
+ private EmailAddressValidator mEmailValidator = new EmailAddressValidator();
+ private Provider mProvider;
private Button mManualButton;
private Button mNextButton;
@@ -168,32 +195,41 @@
setContentView(R.layout.account_setup_basics);
- mFragment = (AccountSetupBasicsFragment)
- getFragmentManager().findFragmentById(R.id.setup_basics_fragment);
- mManualButtonDisplayed = true;
+ mWelcomeView = (TextView) findViewById(R.id.instructions);
+ mEmailView = (EditText) findViewById(R.id.account_email);
+ mPasswordView = (EditText) findViewById(R.id.account_password);
+ mDefaultView = (CheckBox) findViewById(R.id.account_default);
+
+ mEmailView.addTextChangedListener(this);
+ mPasswordView.addTextChangedListener(this);
+
+ // If there are one or more accounts already in existence, then display
+ // the "use as default" checkbox (it defaults to hidden).
+ new DisplayCheckboxTask().execute();
+
+ boolean manualButtonDisplayed = true;
boolean alternateStrings = false;
if (flowMode == SetupData.FLOW_MODE_ACCOUNT_MANAGER_EAS) {
// No need for manual button -> next is appropriate
- mManualButtonDisplayed = false;
+ manualButtonDisplayed = false;
// Swap welcome text for EAS-specific text
alternateStrings = VendorPolicyLoader.getInstance(this).useAlternateExchangeStrings();
setTitle(alternateStrings
? R.string.account_setup_basics_exchange_title_alternate
- : R.string.account_setup_basics_exchange_title);
+ : R.string.account_setup_basics_exchange_title);
+ mWelcomeView.setText(alternateStrings
+ ? R.string.accounts_welcome_exchange_alternate
+ : R.string.accounts_welcome_exchange);
}
- // Configure fragment
- mFragment.setCallback(this, alternateStrings);
-
// Configure buttons
mManualButton = (Button) findViewById(R.id.manual_setup);
mNextButton = (Button) findViewById(R.id.next);
- mManualButton.setVisibility(mManualButtonDisplayed ? View.VISIBLE : View.INVISIBLE);
+ mManualButton.setVisibility(manualButtonDisplayed ? View.VISIBLE : View.INVISIBLE);
mManualButton.setOnClickListener(this);
mNextButton.setOnClickListener(this);
- // Force disabled until fragment notifies otherwise
- mNextButtonEnabled = true;
- this.onEnableProceedButtons(false);
+ // Force disabled until validator notifies otherwise
+ onEnableProceedButtons(false);
mAccountAuthenticatorResponse =
getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
@@ -202,6 +238,18 @@
mAccountAuthenticatorResponse.onRequestContinued();
}
+ // Load fields, but only once
+ String userName = SetupData.getUsername();
+ if (userName != null) {
+ mEmailView.setText(userName);
+ SetupData.setUsername(null);
+ }
+ String password = SetupData.getPassword();
+ if (userName != null) {
+ mPasswordView.setText(password);
+ SetupData.setPassword(null);
+ }
+
// Handle force account creation immediately (now that fragment is set up)
// This is never allowed in a normal user build and will exit immediately.
if (SetupData.getFlowMode() == SetupData.FLOW_MODE_FORCE_CREATE) {
@@ -222,10 +270,14 @@
finish();
return;
}
- mFragment.forceCreateAccount(email, user, incoming, outgoing);
+ forceCreateAccount(email, user, incoming, outgoing);
onCheckSettingsComplete(AccountCheckSettingsFragment.CHECK_SETTINGS_OK); // calls finish
return;
}
+
+ if (savedInstanceState != null && savedInstanceState.containsKey(STATE_KEY_PROVIDER)) {
+ mProvider = (Provider) savedInstanceState.getSerializable(STATE_KEY_PROVIDER);
+ }
}
@Override
@@ -243,6 +295,249 @@
super.finish();
}
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (mProvider != null) {
+ outState.putSerializable(STATE_KEY_PROVIDER, mProvider);
+ }
+ }
+
+ /**
+ * Implements OnClickListener
+ */
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.next:
+ onNext();
+ break;
+ case R.id.manual_setup:
+ onManualSetup(false);
+ break;
+ }
+ }
+
+ /**
+ * Implements TextWatcher
+ */
+ public void afterTextChanged(Editable s) {
+ validateFields();
+ }
+
+ /**
+ * Implements TextWatcher
+ */
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ /**
+ * Implements TextWatcher
+ */
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ private void validateFields() {
+ boolean valid = Utility.isTextViewNotEmpty(mEmailView)
+ && Utility.isTextViewNotEmpty(mPasswordView)
+ && mEmailValidator.isValid(mEmailView.getText().toString().trim());
+ onEnableProceedButtons(valid);
+ }
+
+ /**
+ * TODO this should also be in AsyncTask
+ * TODO figure out another way to get the owner name
+ */
+ private String getOwnerName() {
+ String name = null;
+ long defaultId = Account.getDefaultAccountId(this);
+ if (defaultId != -1) {
+ Account account = Account.restoreAccountWithId(this, defaultId);
+ if (account != null) {
+ name = account.getSenderName();
+ }
+ }
+ return name;
+ }
+
+ /**
+ * Finish the auto setup process, in some cases after showing a warning dialog.
+ */
+ private void finishAutoSetup() {
+ String email = mEmailView.getText().toString().trim();
+ String password = mPasswordView.getText().toString().trim();
+ String[] emailParts = email.split("@");
+ String user = emailParts[0];
+ String domain = emailParts[1];
+ URI incomingUri = null;
+ URI outgoingUri = null;
+ try {
+ String incomingUsername = mProvider.incomingUsernameTemplate;
+ incomingUsername = incomingUsername.replaceAll("\\$email", email);
+ incomingUsername = incomingUsername.replaceAll("\\$user", user);
+ incomingUsername = incomingUsername.replaceAll("\\$domain", domain);
+
+ URI incomingUriTemplate = mProvider.incomingUriTemplate;
+ incomingUri = new URI(incomingUriTemplate.getScheme(), incomingUsername + ":"
+ + password, incomingUriTemplate.getHost(), incomingUriTemplate.getPort(),
+ incomingUriTemplate.getPath(), null, null);
+
+ String outgoingUsername = mProvider.outgoingUsernameTemplate;
+ outgoingUsername = outgoingUsername.replaceAll("\\$email", email);
+ outgoingUsername = outgoingUsername.replaceAll("\\$user", user);
+ outgoingUsername = outgoingUsername.replaceAll("\\$domain", domain);
+
+ URI outgoingUriTemplate = mProvider.outgoingUriTemplate;
+ outgoingUri = new URI(outgoingUriTemplate.getScheme(), outgoingUsername + ":"
+ + password, outgoingUriTemplate.getHost(), outgoingUriTemplate.getPort(),
+ outgoingUriTemplate.getPath(), null, null);
+
+ // Stop here if the login credentials duplicate an existing account
+ // TODO this shouldn't be in UI thread
+ Account account = Utility.findExistingAccount(this, -1,
+ incomingUri.getHost(), incomingUsername);
+ if (account != null) {
+ DuplicateAccountDialogFragment dialogFragment =
+ DuplicateAccountDialogFragment.newInstance(account.mDisplayName);
+ dialogFragment.show(getFragmentManager(), DuplicateAccountDialogFragment.TAG);
+ return;
+ }
+
+ } catch (URISyntaxException use) {
+ /*
+ * If there is some problem with the URI we give up and go on to
+ * manual setup. Technically speaking, AutoDiscover is OK here, since user clicked
+ * "Next" to get here. This would never happen in practice because we don't expect
+ * to find any EAS accounts in the providers list.
+ */
+ onManualSetup(true);
+ return;
+ }
+
+ populateSetupData(getOwnerName(), email, mDefaultView.isChecked(),
+ incomingUri.toString(), outgoingUri.toString());
+
+ /**
+ * Start the account checker fragment
+ * TODO how to link directly from activity<-->fragment
+ */
+ AccountCheckSettingsFragment checkerFragment = AccountCheckSettingsFragment.newInstance(
+ SetupData.CHECK_INCOMING | SetupData.CHECK_OUTGOING, null);
+ FragmentTransaction transaction = getFragmentManager().openTransaction();
+ transaction.add(checkerFragment, AccountCheckSettingsFragment.TAG);
+ transaction.addToBackStack("back");
+ transaction.commit();
+ }
+
+ /**
+ * When "next" button is clicked
+ */
+ private void onNext() {
+ // Try auto-configuration from XML providers (unless in EAS mode, we can skip it)
+ if (SetupData.getFlowMode() != SetupData.FLOW_MODE_ACCOUNT_MANAGER_EAS) {
+ String email = mEmailView.getText().toString().trim();
+ String[] emailParts = email.split("@");
+ String domain = emailParts[1].trim();
+ mProvider = AccountSettingsUtils.findProviderForDomain(this, domain);
+ if (mProvider != null) {
+ if (mProvider.note != null) {
+ NoteDialogFragment dialogFragment =
+ NoteDialogFragment.newInstance(mProvider.note);
+ dialogFragment.show(getFragmentManager(), NoteDialogFragment.TAG);
+ } else {
+ finishAutoSetup();
+ }
+ return;
+ }
+ }
+ // Can't use auto setup (although EAS accounts may still be able to AutoDiscover)
+ onManualSetup(true);
+ }
+
+ /**
+ * When "manual setup" button is clicked
+ *
+ * @param allowAutoDiscover - true if the user clicked 'next' and (if the account is EAS)
+ * it's OK to use autodiscover. false to prevent autodiscover and go straight to manual setup.
+ * Ignored for IMAP & POP accounts.
+ */
+ private void onManualSetup(boolean allowAutoDiscover) {
+ String email = mEmailView.getText().toString().trim();
+ String password = mPasswordView.getText().toString();
+ String[] emailParts = email.split("@");
+ String user = emailParts[0].trim();
+ String domain = emailParts[1].trim();
+
+ // Alternate entry to the debug options screen (for devices without a physical keyboard:
+ // Username: d@d.d
+ // Password: debug
+ if (ENTER_DEBUG_SCREEN && "d@d.d".equals(email) && "debug".equals(password)) {
+ mEmailView.setText("");
+ mPasswordView.setText("");
+ AccountSettingsXL.actionSettingsWithDebug(this);
+ return;
+ }
+
+ String uriString = null;
+ try {
+ URI uri = new URI("placeholder", user + ":" + password, domain, -1, null, null, null);
+ uriString = uri.toString();
+ } catch (URISyntaxException use) {
+ // If we can't set up the URL, don't continue - account setup pages will fail too
+ Toast.makeText(this, R.string.account_setup_username_password_toast,
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ populateSetupData(getOwnerName(), email, mDefaultView.isChecked(), uriString, uriString);
+
+ SetupData.setAllowAutodiscover(allowAutoDiscover);
+ AccountSetupAccountType.actionSelectAccountType(this);
+ }
+
+ /**
+ * To support continuous testing, we allow the forced creation of accounts.
+ * This works in a manner fairly similar to automatic setup, in which the complete server
+ * Uri's are available, except that we will also skip checking (as if both checks were true)
+ * and all other UI.
+ *
+ * @param email The email address for the new account
+ * @param user The user name for the new account
+ * @param incoming The URI-style string defining the incoming account
+ * @param outgoing The URI-style string defining the outgoing account
+ */
+ private void forceCreateAccount(String email, String user, String incoming, String outgoing) {
+ populateSetupData(user, email, false, incoming, outgoing);
+ }
+
+ /**
+ * Populate SetupData's account with complete setup info.
+ */
+ private void populateSetupData(String senderName, String senderEmail, boolean isDefault,
+ String incoming, String outgoing) {
+ Account account = SetupData.getAccount();
+ account.setSenderName(senderName);
+ account.setEmailAddress(senderEmail);
+ account.setDisplayName(senderEmail);
+ account.setDefaultAccount(isDefault);
+ SetupData.setDefault(isDefault); // TODO - why duplicated, if already set in account
+ account.setStoreUri(this, incoming);
+ account.setSenderUri(this, outgoing);
+
+ // Set sync and delete policies for specific account types
+ if (incoming.startsWith("imap")) {
+ // Delete policy must be set explicitly, because IMAP does not provide a UI selection
+ // for it. This logic needs to be followed in the auto setup flow as well.
+ account.setDeletePolicy(EmailContent.Account.DELETE_POLICY_ON_DELETE);
+ }
+
+ if (incoming.startsWith("eas")) {
+ account.setSyncInterval(Account.CHECK_INTERVAL_PUSH);
+ } else {
+ account.setSyncInterval(DEFAULT_ACCOUNT_CHECK_INTERVAL);
+ }
+ }
+
/**
* Implements AccountCheckSettingsFragment.Callbacks
*
@@ -266,67 +561,76 @@
}
/**
- * Implements OnClickListener
+ * AsyncTask checks count of accounts and displays "use this account as default" checkbox
+ * if there are more than one.
*/
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.next:
- mFragment.onNext();
- break;
- case R.id.manual_setup:
- // no AutoDiscover - user clicked "manual"
- mFragment.onManualSetup(false);
- break;
+ private class DisplayCheckboxTask extends AsyncTask<Void, Void, Integer> {
+
+ @Override
+ protected Integer doInBackground(Void... params) {
+ return EmailContent.count(AccountSetupBasics.this, EmailContent.Account.CONTENT_URI);
+ }
+
+ @Override
+ protected void onPostExecute(Integer numAccounts) {
+ if (numAccounts > 0) {
+ Activity activity = AccountSetupBasics.this;
+ activity.findViewById(R.id.account_default_divider_1).setVisibility(View.VISIBLE);
+ mDefaultView.setVisibility(View.VISIBLE);
+ activity.findViewById(R.id.account_default_divider_2).setVisibility(View.VISIBLE);
+ }
}
}
- /**
- * Implements AccountSetupBasicsFragment.Callback
- */
- @Override
- public void onEnableProceedButtons(boolean enabled) {
- boolean wasEnabled = mNextButtonEnabled;
- mNextButtonEnabled = enabled;
+ private void onEnableProceedButtons(boolean enabled) {
+ mManualButton.setEnabled(enabled);
+ mNextButton.setEnabled(enabled);
+ }
- if (enabled != wasEnabled) {
- mManualButton.setEnabled(enabled);
- mNextButton.setEnabled(enabled);
+ /**
+ * Dialog fragment to show "setup note" dialog
+ */
+ public static class NoteDialogFragment extends DialogFragment {
+ private final static String TAG = "NoteDialogFragment";
+
+ // Argument bundle keys
+ private final static String BUNDLE_KEY_NOTE = "NoteDialogFragment.Note";
+
+ /**
+ * Create the dialog with parameters
+ */
+ public static NoteDialogFragment newInstance(String note) {
+ NoteDialogFragment f = new NoteDialogFragment();
+ Bundle b = new Bundle();
+ b.putString(BUNDLE_KEY_NOTE, note);
+ f.setArguments(b);
+ return f;
}
- }
- /**
- * Implements AccountSetupBasicsFragment.Callback
- *
- * This is called when auto-setup (from hardcoded server info) is attempted.
- * Replace the name/password fragment with the account checker, which will begin to
- * check incoming/outgoing.
- */
- @Override
- public void onProceedAutomatic() {
- AccountCheckSettingsFragment checkerFragment =
- AccountCheckSettingsFragment.newInstance(
- SetupData.CHECK_INCOMING | SetupData.CHECK_OUTGOING, null);
- FragmentTransaction transaction = getFragmentManager().openTransaction();
- transaction.replace(R.id.setup_basics_fragment, checkerFragment);
- transaction.addToBackStack("back");
- transaction.commit();
- }
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Context context = getActivity();
+ final String note = getArguments().getString(BUNDLE_KEY_NOTE);
- /**
- * Implements AccountSetupBasicsFragment.Callback
- */
- @Override
- public void onProceedDebugSettings() {
- AccountSettingsXL.actionSettingsWithDebug(this);
- }
-
- /**
- * Implements AccountSetupBasicsFragment.Callback
- */
- @Override
- public void onProceedManual(boolean allowAutoDiscover) {
- SetupData.setAllowAutodiscover(allowAutoDiscover);
- AccountSetupAccountType.actionSelectAccountType(this);
+ return new AlertDialog.Builder(context)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setTitle(android.R.string.dialog_alert_title)
+ .setMessage(note)
+ .setPositiveButton(
+ R.string.okay_action,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ Activity a = getActivity();
+ if (a instanceof AccountSetupBasics) {
+ ((AccountSetupBasics)a).finishAutoSetup();
+ }
+ dismiss();
+ }
+ })
+ .setNegativeButton(
+ context.getString(R.string.cancel_action),
+ null)
+ .create();
+ }
}
}
diff --git a/src/com/android/email/activity/setup/AccountSetupBasicsFragment.java b/src/com/android/email/activity/setup/AccountSetupBasicsFragment.java
deleted file mode 100644
index ce1e159..0000000
--- a/src/com/android/email/activity/setup/AccountSetupBasicsFragment.java
+++ /dev/null
@@ -1,538 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.email.activity.setup;
-
-import com.android.email.Email;
-import com.android.email.EmailAddressValidator;
-import com.android.email.R;
-import com.android.email.Utility;
-import com.android.email.activity.setup.AccountSettingsUtils.Provider;
-import com.android.email.provider.EmailContent;
-import com.android.email.provider.EmailContent.Account;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CheckBox;
-import android.widget.EditText;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-
-/**
- * Prompts the user for the email address and password. Also prompts for
- * "Use this account as default" if this is the 2nd+ account being set up.
- * Attempts to lookup default settings for the domain the user specified. If the
- * domain is known the settings are handed off to the AccountSetupCheckSettings
- * activity. If no settings are found the settings are handed off to the
- * AccountSetupAccountType activity.
- *
- * TODO: Move provider lookups to AsyncTask(s)
- * TODO: Confirm not broken on Phone UX
- */
-public class AccountSetupBasicsFragment extends Fragment implements TextWatcher {
- private final static boolean ENTER_DEBUG_SCREEN = true;
-
- private final static String STATE_KEY_PROVIDER =
- "com.android.email.AccountSetupBasics.provider";
-
- // NOTE: If you change this value, confirm that the new interval exists in arrays.xml
- private final static int DEFAULT_ACCOUNT_CHECK_INTERVAL = 15;
-
- // Support for UI
- private TextView mWelcomeView;
- private EditText mEmailView;
- private EditText mPasswordView;
- private CheckBox mDefaultView;
- private EmailAddressValidator mEmailValidator = new EmailAddressValidator();
- private Provider mProvider;
-
- // Support for lifecycle
- private Context mContext;
- private Callback mCallback = EmptyCallback.INSTANCE;
- private boolean mStarted;
- private boolean mLoaded;
- private boolean mUseAlternateStrings;
-
- /**
- * Callback interface that owning activities must implement
- */
- public interface Callback {
- public void onEnableProceedButtons(boolean enable);
- public void onProceedAutomatic();
- public void onProceedManual(boolean allowAutoDiscover);
- public void onProceedDebugSettings();
- }
-
- private static class EmptyCallback implements Callback {
- public static final Callback INSTANCE = new EmptyCallback();
- @Override public void onEnableProceedButtons(boolean enable) { }
- @Override public void onProceedAutomatic() { }
- @Override public void onProceedManual(boolean allowAutoDiscover) { }
- @Override public void onProceedDebugSettings() { }
- }
-
- /**
- * Called to do initial creation of a fragment. This is called after
- * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}.
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
- Log.d(Email.LOG_TAG, "AccountSetupBasicsFragment onCreate");
- }
- super.onCreate(savedInstanceState);
-
- if (savedInstanceState != null && savedInstanceState.containsKey(STATE_KEY_PROVIDER)) {
- mProvider = (Provider) savedInstanceState.getSerializable(STATE_KEY_PROVIDER);
- }
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
- Log.d(Email.LOG_TAG, "AccountSetupBasicsFragment onCreateView");
- }
- View view = inflater.inflate(R.layout.account_setup_basics_fragment, container, false);
-
- mWelcomeView = (TextView) view.findViewById(R.id.instructions);
- mEmailView = (EditText) view.findViewById(R.id.account_email);
- mPasswordView = (EditText) view.findViewById(R.id.account_password);
- mDefaultView = (CheckBox) view.findViewById(R.id.account_default);
-
- mEmailView.addTextChangedListener(this);
- mPasswordView.addTextChangedListener(this);
-
- // If there are one or more accounts already in existence, then display
- // the "use as default" checkbox (it defaults to hidden).
- new DisplayCheckboxTask().execute();
-
- return view;
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
- Log.d(Email.LOG_TAG, "AccountSetupBasicsFragment onActivityCreated");
- }
- super.onActivityCreated(savedInstanceState);
- }
-
- /**
- * Called when the Fragment is visible to the user.
- */
- @Override
- public void onStart() {
- if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
- Log.d(Email.LOG_TAG, "AccountSetupBasicsFragment onStart");
- }
- super.onStart();
- mStarted = true;
- if (!mLoaded) {
- loadSettings();
- }
- }
-
- /**
- * Called when the fragment is visible to the user and actively running.
- */
- @Override
- public void onResume() {
- if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
- Log.d(Email.LOG_TAG, "AccountSetupBasicsFragment onResume");
- }
- super.onResume();
- validateFields();
- }
-
- @Override
- public void onPause() {
- if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
- Log.d(Email.LOG_TAG, "AccountSetupBasicsFragment onPause");
- }
- super.onPause();
- }
-
- /**
- * Called when the Fragment is no longer started.
- */
- @Override
- public void onStop() {
- if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
- Log.d(Email.LOG_TAG, "AccountSetupBasicsFragment onStop");
- }
- super.onStop();
- mStarted = false;
- }
-
- /**
- * Called when the fragment is no longer in use.
- */
- @Override
- public void onDestroy() {
- if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
- Log.d(Email.LOG_TAG, "AccountSetupBasicsFragment onDestroy");
- }
- super.onDestroy();
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
- Log.d(Email.LOG_TAG, "AccountSetupBasicsFragment onSaveInstanceState");
- }
- super.onSaveInstanceState(outState);
- if (mProvider != null) {
- outState.putSerializable(STATE_KEY_PROVIDER, mProvider);
- }
- }
-
- /**
- * Activity provides callbacks here. This also triggers loading and setting up the UX
- */
- public void setCallback(Callback callback, boolean useAlternateStrings) {
- mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback;
- mUseAlternateStrings = useAlternateStrings;
- mContext = getActivity();
- if (mStarted && !mLoaded) {
- loadSettings();
- }
- }
-
- /**
- * Load account data into preference UI
- */
- private void loadSettings() {
- // We can only do this once, so prevent repeat
- mLoaded = true;
-
- int flowMode = SetupData.getFlowMode();
- if (flowMode == SetupData.FLOW_MODE_ACCOUNT_MANAGER_EAS) {
- // Swap welcome text for EAS-specific text
- mWelcomeView.setText(mUseAlternateStrings
- ? R.string.accounts_welcome_exchange_alternate
- : R.string.accounts_welcome_exchange);
- }
-
- if (SetupData.getUsername() != null) {
- mEmailView.setText(SetupData.getUsername());
- }
- if (SetupData.getPassword() != null) {
- mPasswordView.setText(SetupData.getPassword());
- }
- }
-
- 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) {
- }
-
- private void validateFields() {
- boolean valid = Utility.isTextViewNotEmpty(mEmailView)
- && Utility.isTextViewNotEmpty(mPasswordView)
- && mEmailValidator.isValid(mEmailView.getText().toString().trim());
- mCallback.onEnableProceedButtons(valid);
- }
-
- // TODO this should also be in AsyncTask
- private String getOwnerName() {
- String name = null;
-/* TODO figure out another way to get the owner name
- String projection[] = {
- ContactMethods.NAME
- };
- Cursor c = getContentResolver().query(
- Uri.withAppendedPath(Contacts.People.CONTENT_URI, "owner"), projection, null, null,
- null);
- if (c != null) {
- if (c.moveToFirst()) {
- name = c.getString(0);
- }
- c.close();
- }
-*/
-
- if (name == null || name.length() == 0) {
- long defaultId = Account.getDefaultAccountId(mContext);
- if (defaultId != -1) {
- Account account = Account.restoreAccountWithId(mContext, defaultId);
- if (account != null) {
- name = account.getSenderName();
- }
- }
- }
- return name;
- }
-
- /**
- * AsyncTask checks count of accounts and displays "use this account as default" checkbox
- * if there are more than one.
- */
- private class DisplayCheckboxTask extends AsyncTask<Void, Void, Integer> {
-
- @Override
- protected Integer doInBackground(Void... params) {
- return EmailContent.count(getActivity(), EmailContent.Account.CONTENT_URI);
- }
-
- @Override
- protected void onPostExecute(Integer numAccounts) {
- if (numAccounts > 0) {
- View container = AccountSetupBasicsFragment.this.getView();
- container.findViewById(R.id.account_default_divider_1).setVisibility(View.VISIBLE);
- mDefaultView.setVisibility(View.VISIBLE);
- container.findViewById(R.id.account_default_divider_2).setVisibility(View.VISIBLE);
- }
- }
- }
-
- /**
- * Finish the auto setup process, in some cases after showing a warning dialog.
- */
- private void finishAutoSetup() {
- String email = mEmailView.getText().toString().trim();
- String password = mPasswordView.getText().toString().trim();
- String[] emailParts = email.split("@");
- String user = emailParts[0];
- String domain = emailParts[1];
- URI incomingUri = null;
- URI outgoingUri = null;
- try {
- String incomingUsername = mProvider.incomingUsernameTemplate;
- incomingUsername = incomingUsername.replaceAll("\\$email", email);
- incomingUsername = incomingUsername.replaceAll("\\$user", user);
- incomingUsername = incomingUsername.replaceAll("\\$domain", domain);
-
- URI incomingUriTemplate = mProvider.incomingUriTemplate;
- incomingUri = new URI(incomingUriTemplate.getScheme(), incomingUsername + ":"
- + password, incomingUriTemplate.getHost(), incomingUriTemplate.getPort(),
- incomingUriTemplate.getPath(), null, null);
-
- String outgoingUsername = mProvider.outgoingUsernameTemplate;
- outgoingUsername = outgoingUsername.replaceAll("\\$email", email);
- outgoingUsername = outgoingUsername.replaceAll("\\$user", user);
- outgoingUsername = outgoingUsername.replaceAll("\\$domain", domain);
-
- URI outgoingUriTemplate = mProvider.outgoingUriTemplate;
- outgoingUri = new URI(outgoingUriTemplate.getScheme(), outgoingUsername + ":"
- + password, outgoingUriTemplate.getHost(), outgoingUriTemplate.getPort(),
- outgoingUriTemplate.getPath(), null, null);
-
- // Stop here if the login credentials duplicate an existing account
- // TODO this shouldn't be in UI thread
- Account account = Utility.findExistingAccount(mContext, -1,
- incomingUri.getHost(), incomingUsername);
- if (account != null) {
- DuplicateAccountDialogFragment dialogFragment =
- DuplicateAccountDialogFragment.newInstance(account.mDisplayName);
- dialogFragment.show(getActivity(), DuplicateAccountDialogFragment.TAG);
- return;
- }
-
- } catch (URISyntaxException use) {
- /*
- * If there is some problem with the URI we give up and go on to
- * manual setup. Technically speaking, AutoDiscover is OK here, since user clicked
- * "Next" to get here. This would never happen in practice because we don't expect
- * to find any EAS accounts in the providers list.
- */
- onManualSetup(true);
- return;
- }
-
- populateSetupData(getOwnerName(), email, mDefaultView.isChecked(),
- incomingUri.toString(), outgoingUri.toString());
-
- mCallback.onProceedAutomatic();
- }
-
- /**
- * Entry point from Activity, when "next" button is clicked
- */
- public void onNext() {
- // Try auto-configuration from XML providers (unless in EAS mode, we can skip it)
- if (SetupData.getFlowMode() != SetupData.FLOW_MODE_ACCOUNT_MANAGER_EAS) {
- String email = mEmailView.getText().toString().trim();
- String[] emailParts = email.split("@");
- String domain = emailParts[1].trim();
- mProvider = AccountSettingsUtils.findProviderForDomain(mContext, domain);
- if (mProvider != null) {
- if (mProvider.note != null) {
- NoteDialogFragment dialogFragment =
- NoteDialogFragment.newInstance(mProvider.note, this);
- dialogFragment.show(getActivity(), NoteDialogFragment.TAG);
- } else {
- finishAutoSetup();
- }
- return;
- }
- }
- // Can't use auto setup (although EAS accounts may still be able to AutoDiscover)
- onManualSetup(true);
- }
-
- /**
- * Entry point from Activity, when "manual setup" button is clicked
- *
- * @param allowAutoDiscover - true if the user clicked 'next' and (if the account is EAS)
- * it's OK to use autodiscover. false to prevent autodiscover and go straight to manual setup.
- * Ignored for IMAP & POP accounts.
- */
- public void onManualSetup(boolean allowAutoDiscover) {
- String email = mEmailView.getText().toString().trim();
- String password = mPasswordView.getText().toString();
- String[] emailParts = email.split("@");
- String user = emailParts[0].trim();
- String domain = emailParts[1].trim();
-
- // Alternate entry to the debug options screen (for devices without a physical keyboard:
- // Username: d@d.d
- // Password: debug
- if (ENTER_DEBUG_SCREEN && "d@d.d".equals(email) && "debug".equals(password)) {
- mEmailView.setText("");
- mPasswordView.setText("");
- mCallback.onProceedDebugSettings();
- return;
- }
-
- String uriString = null;
- try {
- URI uri = new URI("placeholder", user + ":" + password, domain, -1, null, null, null);
- uriString = uri.toString();
- } catch (URISyntaxException use) {
- // If we can't set up the URL, don't continue - account setup pages will fail too
- Toast.makeText(mContext, R.string.account_setup_username_password_toast,
- Toast.LENGTH_LONG).show();
- return;
- }
-
- populateSetupData(getOwnerName(), email, mDefaultView.isChecked(), uriString, uriString);
-
- mCallback.onProceedManual(allowAutoDiscover);
- }
-
- /**
- * To support continuous testing, we allow the forced creation of accounts.
- * This works in a manner fairly similar to automatic setup, in which the complete server
- * Uri's are available, except that we will also skip checking (as if both checks were true)
- * and all other UI.
- *
- * @param email The email address for the new account
- * @param user The user name for the new account
- * @param incoming The URI-style string defining the incoming account
- * @param outgoing The URI-style string defining the outgoing account
- */
- public void forceCreateAccount(String email, String user, String incoming, String outgoing) {
- populateSetupData(user, email, false, incoming, outgoing);
- }
-
- /**
- * Populate SetupData's account with complete setup info.
- */
- private void populateSetupData(String senderName, String senderEmail, boolean isDefault,
- String incoming, String outgoing) {
- Account account = SetupData.getAccount();
- account.setSenderName(senderName);
- account.setEmailAddress(senderEmail);
- account.setDisplayName(senderEmail);
- account.setDefaultAccount(isDefault);
- SetupData.setDefault(isDefault); // TODO - why duplicated, if already set in account
- account.setStoreUri(mContext, incoming);
- account.setSenderUri(mContext, outgoing);
-
- // Set sync and delete policies for specific account types
- if (incoming.startsWith("imap")) {
- // Delete policy must be set explicitly, because IMAP does not provide a UI selection
- // for it. This logic needs to be followed in the auto setup flow as well.
- account.setDeletePolicy(EmailContent.Account.DELETE_POLICY_ON_DELETE);
- }
-
- if (incoming.startsWith("eas")) {
- account.setSyncInterval(Account.CHECK_INTERVAL_PUSH);
- } else {
- account.setSyncInterval(DEFAULT_ACCOUNT_CHECK_INTERVAL);
- }
- }
-
- /**
- * Dialog fragment to show "setup note" dialog
- */
- public static class NoteDialogFragment extends DialogFragment {
- private final static String TAG = "NoteDialogFragment";
-
- // Argument bundle keys
- private final static String BUNDLE_KEY_NOTE = "NoteDialogFragment.Note";
-
- /**
- * Create the dialog with parameters
- */
- public static NoteDialogFragment newInstance(String note, Fragment parentFragment) {
- NoteDialogFragment f = new NoteDialogFragment();
- Bundle b = new Bundle();
- b.putString(BUNDLE_KEY_NOTE, note);
- f.setArguments(b);
- f.setTargetFragment(parentFragment, 0);
- return f;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- Context context = getActivity();
- final String note = getArguments().getString(BUNDLE_KEY_NOTE);
-
- return new AlertDialog.Builder(context)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setTitle(android.R.string.dialog_alert_title)
- .setMessage(note)
- .setPositiveButton(
- R.string.okay_action,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- Fragment f = getTargetFragment();
- if (f instanceof AccountSetupBasicsFragment) {
- ((AccountSetupBasicsFragment)f).finishAutoSetup();
- }
- dismiss();
- }
- })
- .setNegativeButton(
- context.getString(R.string.cancel_action),
- null)
- .create();
- }
- }
-}