Home | History | Annotate | Download | only in setup
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.email.activity.setup;
     18 
     19 import android.accounts.AccountAuthenticatorResponse;
     20 import android.accounts.AccountManager;
     21 import android.accounts.AccountManagerCallback;
     22 import android.accounts.AccountManagerFuture;
     23 import android.accounts.AuthenticatorException;
     24 import android.accounts.OperationCanceledException;
     25 import android.app.Activity;
     26 import android.app.AlertDialog;
     27 import android.content.Context;
     28 import android.content.DialogInterface;
     29 import android.content.Intent;
     30 import android.os.Bundle;
     31 import android.util.Log;
     32 import android.view.View;
     33 import android.view.View.OnClickListener;
     34 import android.widget.ArrayAdapter;
     35 import android.widget.CheckBox;
     36 import android.widget.Spinner;
     37 
     38 import com.android.email.Email;
     39 import com.android.email.R;
     40 import com.android.email.activity.ActivityHelper;
     41 import com.android.email.activity.UiUtilities;
     42 import com.android.email.service.EmailServiceUtils;
     43 import com.android.email.service.MailService;
     44 import com.android.emailcommon.Logging;
     45 import com.android.emailcommon.provider.Account;
     46 import com.android.emailcommon.provider.HostAuth;
     47 import com.android.emailcommon.service.SyncWindow;
     48 import com.android.emailcommon.utility.Utility;
     49 
     50 import java.io.IOException;
     51 
     52 /**
     53  * TODO: Cleanup the manipulation of Account.FLAGS_INCOMPLETE and make sure it's never left set.
     54  */
     55 public class AccountSetupOptions extends AccountSetupActivity implements OnClickListener {
     56 
     57     private Spinner mCheckFrequencyView;
     58     private Spinner mSyncWindowView;
     59     private CheckBox mDefaultView;
     60     private CheckBox mNotifyView;
     61     private CheckBox mSyncContactsView;
     62     private CheckBox mSyncCalendarView;
     63     private CheckBox mSyncEmailView;
     64     private CheckBox mBackgroundAttachmentsView;
     65     private View mAccountSyncWindowRow;
     66     private boolean mDonePressed = false;
     67 
     68     public static final int REQUEST_CODE_ACCEPT_POLICIES = 1;
     69 
     70     /** Default sync window for new EAS accounts */
     71     private static final int SYNC_WINDOW_EAS_DEFAULT = SyncWindow.SYNC_WINDOW_AUTO;
     72 
     73     public static void actionOptions(Activity fromActivity) {
     74         fromActivity.startActivity(new Intent(fromActivity, AccountSetupOptions.class));
     75     }
     76 
     77     @Override
     78     public void onCreate(Bundle savedInstanceState) {
     79         super.onCreate(savedInstanceState);
     80         ActivityHelper.debugSetWindowFlags(this);
     81         setContentView(R.layout.account_setup_options);
     82 
     83         mCheckFrequencyView = (Spinner) UiUtilities.getView(this, R.id.account_check_frequency);
     84         mSyncWindowView = (Spinner) UiUtilities.getView(this, R.id.account_sync_window);
     85         mDefaultView = (CheckBox) UiUtilities.getView(this, R.id.account_default);
     86         mNotifyView = (CheckBox) UiUtilities.getView(this, R.id.account_notify);
     87         mSyncContactsView = (CheckBox) UiUtilities.getView(this, R.id.account_sync_contacts);
     88         mSyncCalendarView = (CheckBox) UiUtilities.getView(this, R.id.account_sync_calendar);
     89         mSyncEmailView = (CheckBox) UiUtilities.getView(this, R.id.account_sync_email);
     90         mSyncEmailView.setChecked(true);
     91         mBackgroundAttachmentsView = (CheckBox) UiUtilities.getView(this,
     92                 R.id.account_background_attachments);
     93         mBackgroundAttachmentsView.setChecked(true);
     94         UiUtilities.getView(this, R.id.previous).setOnClickListener(this);
     95         UiUtilities.getView(this, R.id.next).setOnClickListener(this);
     96         mAccountSyncWindowRow = UiUtilities.getView(this, R.id.account_sync_window_row);
     97 
     98         // Generate spinner entries using XML arrays used by the preferences
     99         int frequencyValuesId;
    100         int frequencyEntriesId;
    101         Account account = SetupData.getAccount();
    102         String protocol = account.mHostAuthRecv.mProtocol;
    103         boolean eas = HostAuth.SCHEME_EAS.equals(protocol);
    104         if (eas) {
    105             frequencyValuesId = R.array.account_settings_check_frequency_values_push;
    106             frequencyEntriesId = R.array.account_settings_check_frequency_entries_push;
    107         } else {
    108             frequencyValuesId = R.array.account_settings_check_frequency_values;
    109             frequencyEntriesId = R.array.account_settings_check_frequency_entries;
    110         }
    111         CharSequence[] frequencyValues = getResources().getTextArray(frequencyValuesId);
    112         CharSequence[] frequencyEntries = getResources().getTextArray(frequencyEntriesId);
    113 
    114         // Now create the array used by the Spinner
    115         SpinnerOption[] checkFrequencies = new SpinnerOption[frequencyEntries.length];
    116         for (int i = 0; i < frequencyEntries.length; i++) {
    117             checkFrequencies[i] = new SpinnerOption(
    118                     Integer.valueOf(frequencyValues[i].toString()), frequencyEntries[i].toString());
    119         }
    120 
    121         ArrayAdapter<SpinnerOption> checkFrequenciesAdapter = new ArrayAdapter<SpinnerOption>(this,
    122                 android.R.layout.simple_spinner_item, checkFrequencies);
    123         checkFrequenciesAdapter
    124                 .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    125         mCheckFrequencyView.setAdapter(checkFrequenciesAdapter);
    126 
    127         if (eas) {
    128             enableEASSyncWindowSpinner();
    129         }
    130 
    131         // Note:  It is OK to use mAccount.mIsDefault here *only* because the account
    132         // has not been written to the DB yet.  Ordinarily, call Account.getDefaultAccountId().
    133         if (account.mIsDefault || SetupData.isDefault()) {
    134             mDefaultView.setChecked(true);
    135         }
    136         mNotifyView.setChecked(
    137                 (account.getFlags() & Account.FLAGS_NOTIFY_NEW_MAIL) != 0);
    138         SpinnerOption.setSpinnerOptionValue(mCheckFrequencyView, account.getSyncInterval());
    139 
    140         // Setup any additional items to support EAS & EAS flow mode
    141         if (eas) {
    142             // "also sync contacts" == "true"
    143             mSyncContactsView.setVisibility(View.VISIBLE);
    144             mSyncContactsView.setChecked(true);
    145             mSyncCalendarView.setVisibility(View.VISIBLE);
    146             mSyncCalendarView.setChecked(true);
    147             // Show the associated dividers
    148             UiUtilities.setVisibilitySafe(this, R.id.account_sync_contacts_divider, View.VISIBLE);
    149             UiUtilities.setVisibilitySafe(this, R.id.account_sync_calendar_divider, View.VISIBLE);
    150         }
    151 
    152         // If we are in POP3, hide the "Background Attachments" mode
    153         if (HostAuth.SCHEME_POP3.equals(protocol)) {
    154             mBackgroundAttachmentsView.setVisibility(View.GONE);
    155             UiUtilities.setVisibilitySafe(this, R.id.account_background_attachments_divider,
    156                     View.GONE);
    157         }
    158 
    159         // If we are just visiting here to fill in details, exit immediately
    160         if (SetupData.isAutoSetup() ||
    161                 SetupData.getFlowMode() == SetupData.FLOW_MODE_FORCE_CREATE) {
    162             onDone();
    163         }
    164     }
    165 
    166     @Override
    167     public void finish() {
    168         // If the account manager initiated the creation, and success was not reported,
    169         // then we assume that we're giving up (for any reason) - report failure.
    170         AccountAuthenticatorResponse authenticatorResponse =
    171             SetupData.getAccountAuthenticatorResponse();
    172         if (authenticatorResponse != null) {
    173             authenticatorResponse.onError(AccountManager.ERROR_CODE_CANCELED, "canceled");
    174             SetupData.setAccountAuthenticatorResponse(null);
    175         }
    176         super.finish();
    177     }
    178 
    179     /**
    180      * Respond to clicks in the "Next" or "Previous" buttons
    181      */
    182     @Override
    183     public void onClick(View view) {
    184         switch (view.getId()) {
    185             case R.id.next:
    186                 // Don't allow this more than once (Exchange accounts call an async method
    187                 // before finish()'ing the Activity, which allows this code to potentially be
    188                 // executed multiple times
    189                 if (!mDonePressed) {
    190                     onDone();
    191                     mDonePressed = true;
    192                 }
    193                 break;
    194             case R.id.previous:
    195                 onBackPressed();
    196                 break;
    197         }
    198     }
    199 
    200     /**
    201      * Ths is called when the user clicks the "done" button.
    202      * It collects the data from the UI, updates the setup account record, and commits
    203      * the account to the database (making it real for the first time.)
    204      * Finally, we call setupAccountManagerAccount(), which will eventually complete via callback.
    205      */
    206     private void onDone() {
    207         final Account account = SetupData.getAccount();
    208         if (account.isSaved()) {
    209             // Disrupting the normal flow could get us here, but if the account is already
    210             // saved, we've done this work
    211             return;
    212         }
    213         account.setDisplayName(account.getEmailAddress());
    214         int newFlags = account.getFlags() &
    215             ~(Account.FLAGS_NOTIFY_NEW_MAIL | Account.FLAGS_BACKGROUND_ATTACHMENTS);
    216         if (mNotifyView.isChecked()) {
    217             newFlags |= Account.FLAGS_NOTIFY_NEW_MAIL;
    218         }
    219         if (mBackgroundAttachmentsView.isChecked()) {
    220             newFlags |= Account.FLAGS_BACKGROUND_ATTACHMENTS;
    221         }
    222         account.setFlags(newFlags);
    223         account.setSyncInterval((Integer)((SpinnerOption)mCheckFrequencyView
    224                 .getSelectedItem()).value);
    225         if (mAccountSyncWindowRow.getVisibility() == View.VISIBLE) {
    226             int window = (Integer)((SpinnerOption)mSyncWindowView.getSelectedItem()).value;
    227             account.setSyncLookback(window);
    228         }
    229         account.setDefaultAccount(mDefaultView.isChecked());
    230 
    231         if (account.mHostAuthRecv == null) {
    232             throw new IllegalStateException("in AccountSetupOptions with null mHostAuthRecv");
    233         }
    234 
    235         // Finish setting up the account, and commit it to the database
    236         // Set the incomplete flag here to avoid reconciliation issues in ExchangeService
    237         account.mFlags |= Account.FLAGS_INCOMPLETE;
    238         boolean calendar = false;
    239         boolean contacts = false;
    240         boolean email = mSyncEmailView.isChecked();
    241         if (account.mHostAuthRecv.mProtocol.equals("eas")) {
    242             if (SetupData.getPolicy() != null) {
    243                 account.mFlags |= Account.FLAGS_SECURITY_HOLD;
    244                 account.mPolicy = SetupData.getPolicy();
    245             }
    246             // Get flags for contacts/calendar sync
    247             contacts = mSyncContactsView.isChecked();
    248             calendar = mSyncCalendarView.isChecked();
    249         }
    250 
    251         // Finally, write the completed account (for the first time) and then
    252         // install it into the Account manager as well.  These are done off-thread.
    253         // The account manager will report back via the callback, which will take us to
    254         // the next operations.
    255         final boolean email2 = email;
    256         final boolean calendar2 = calendar;
    257         final boolean contacts2 = contacts;
    258         Utility.runAsync(new Runnable() {
    259             @Override
    260             public void run() {
    261                 Context context = AccountSetupOptions.this;
    262                 AccountSettingsUtils.commitSettings(context, account);
    263                 MailService.setupAccountManagerAccount(context, account,
    264                         email2, calendar2, contacts2, mAccountManagerCallback);
    265             }
    266         });
    267     }
    268 
    269     /**
    270      * This is called at the completion of MailService.setupAccountManagerAccount()
    271      */
    272     AccountManagerCallback<Bundle> mAccountManagerCallback = new AccountManagerCallback<Bundle>() {
    273         public void run(AccountManagerFuture<Bundle> future) {
    274             try {
    275                 Bundle bundle = future.getResult();
    276                 bundle.keySet();
    277                 AccountSetupOptions.this.runOnUiThread(new Runnable() {
    278                     public void run() {
    279                         optionsComplete();
    280                     }
    281                 });
    282                 return;
    283             } catch (OperationCanceledException e) {
    284                 Log.d(Logging.LOG_TAG, "addAccount was canceled");
    285             } catch (IOException e) {
    286                 Log.d(Logging.LOG_TAG, "addAccount failed: " + e);
    287             } catch (AuthenticatorException e) {
    288                 Log.d(Logging.LOG_TAG, "addAccount failed: " + e);
    289             }
    290             showErrorDialog(R.string.account_setup_failed_dlg_auth_message,
    291                     R.string.system_account_create_failed);
    292         }
    293     };
    294 
    295     /**
    296      * This is called if MailService.setupAccountManagerAccount() fails for some reason
    297      */
    298     private void showErrorDialog(final int msgResId, final Object... args) {
    299         runOnUiThread(new Runnable() {
    300             public void run() {
    301                 new AlertDialog.Builder(AccountSetupOptions.this)
    302                         .setIconAttribute(android.R.attr.alertDialogIcon)
    303                         .setTitle(getString(R.string.account_setup_failed_dlg_title))
    304                         .setMessage(getString(msgResId, args))
    305                         .setCancelable(true)
    306                         .setPositiveButton(
    307                                 getString(R.string.account_setup_failed_dlg_edit_details_action),
    308                                 new DialogInterface.OnClickListener() {
    309                                     public void onClick(DialogInterface dialog, int which) {
    310                                        finish();
    311                                     }
    312                                 })
    313                         .show();
    314             }
    315         });
    316     }
    317 
    318     /**
    319      * This is called after the account manager creates the new account.
    320      */
    321     private void optionsComplete() {
    322         // If the account manager initiated the creation, report success at this point
    323         AccountAuthenticatorResponse authenticatorResponse =
    324             SetupData.getAccountAuthenticatorResponse();
    325         if (authenticatorResponse != null) {
    326             authenticatorResponse.onResult(null);
    327             SetupData.setAccountAuthenticatorResponse(null);
    328         }
    329 
    330         // Now that AccountManager account creation is complete, clear the INCOMPLETE flag
    331         Account account = SetupData.getAccount();
    332         account.mFlags &= ~Account.FLAGS_INCOMPLETE;
    333         AccountSettingsUtils.commitSettings(AccountSetupOptions.this, account);
    334 
    335         // If we've got policies for this account, ask the user to accept.
    336         if ((account.mFlags & Account.FLAGS_SECURITY_HOLD) != 0) {
    337             Intent intent = AccountSecurity.actionUpdateSecurityIntent(this, account.mId, false);
    338             startActivityForResult(intent, AccountSetupOptions.REQUEST_CODE_ACCEPT_POLICIES);
    339             return;
    340         }
    341         saveAccountAndFinish();
    342     }
    343 
    344     /**
    345      * This is called after the AccountSecurity activity completes.
    346      */
    347     @Override
    348     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    349         saveAccountAndFinish();
    350     }
    351 
    352     /**
    353      * These are the final cleanup steps when creating an account:
    354      *  Clear incomplete & security hold flags
    355      *  Update account in DB
    356      *  Enable email services
    357      *  Enable exchange services
    358      *  Move to final setup screen
    359      */
    360     private void saveAccountAndFinish() {
    361         Utility.runAsync(new Runnable() {
    362             @Override
    363             public void run() {
    364                 AccountSetupOptions context = AccountSetupOptions.this;
    365                 // Clear the security hold flag now
    366                 Account account = SetupData.getAccount();
    367                 account.mFlags &= ~Account.FLAGS_SECURITY_HOLD;
    368                 AccountSettingsUtils.commitSettings(context, account);
    369                 // Start up services based on new account(s)
    370                 Email.setServicesEnabledSync(context);
    371                 EmailServiceUtils.startExchangeService(context);
    372                 // Move to final setup screen
    373                 AccountSetupNames.actionSetNames(context);
    374                 finish();
    375             }
    376         });
    377     }
    378 
    379     /**
    380      * Enable an additional spinner using the arrays normally handled by preferences
    381      */
    382     private void enableEASSyncWindowSpinner() {
    383         // Show everything
    384         mAccountSyncWindowRow.setVisibility(View.VISIBLE);
    385 
    386         // Generate spinner entries using XML arrays used by the preferences
    387         CharSequence[] windowValues = getResources().getTextArray(
    388                 R.array.account_settings_mail_window_values);
    389         CharSequence[] windowEntries = getResources().getTextArray(
    390                 R.array.account_settings_mail_window_entries);
    391 
    392         // Now create the array used by the Spinner
    393         SpinnerOption[] windowOptions = new SpinnerOption[windowEntries.length];
    394         int defaultIndex = -1;
    395         for (int i = 0; i < windowEntries.length; i++) {
    396             final int value = Integer.valueOf(windowValues[i].toString());
    397             windowOptions[i] = new SpinnerOption(value, windowEntries[i].toString());
    398             if (value == SYNC_WINDOW_EAS_DEFAULT) {
    399                 defaultIndex = i;
    400             }
    401         }
    402 
    403         ArrayAdapter<SpinnerOption> windowOptionsAdapter = new ArrayAdapter<SpinnerOption>(this,
    404                 android.R.layout.simple_spinner_item, windowOptions);
    405         windowOptionsAdapter
    406                 .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    407         mSyncWindowView.setAdapter(windowOptionsAdapter);
    408 
    409         SpinnerOption.setSpinnerOptionValue(mSyncWindowView,
    410                 SetupData.getAccount().getSyncLookback());
    411         if (defaultIndex >= 0) {
    412             mSyncWindowView.setSelection(defaultIndex);
    413         }
    414     }
    415 }
    416