AccountSetupOptions.java revision ec9398e7c6dc7a016b68ea3009fd950f898763a0
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 17package com.android.email.activity.setup; 18 19import com.android.email.Email; 20import com.android.email.ExchangeUtils; 21import com.android.email.R; 22import com.android.email.SecurityPolicy.PolicySet; 23import com.android.email.Utility; 24import com.android.email.activity.ActivityHelper; 25import com.android.email.mail.Store; 26import com.android.email.provider.EmailContent; 27import com.android.email.provider.EmailContent.Account; 28import com.android.email.service.MailService; 29 30import android.accounts.AccountManagerCallback; 31import android.accounts.AccountManagerFuture; 32import android.accounts.AuthenticatorException; 33import android.accounts.OperationCanceledException; 34import android.app.Activity; 35import android.app.AlertDialog; 36import android.content.Context; 37import android.content.DialogInterface; 38import android.content.Intent; 39import android.os.Bundle; 40import android.util.Log; 41import android.view.View; 42import android.view.View.OnClickListener; 43import android.widget.ArrayAdapter; 44import android.widget.CheckBox; 45import android.widget.Spinner; 46 47import java.io.IOException; 48 49/** 50 * TODO: Cleanup the manipulation of Account.FLAGS_INCOMPLETE and make sure it's never left set. 51 */ 52public class AccountSetupOptions extends AccountSetupActivity implements OnClickListener { 53 54 private Spinner mCheckFrequencyView; 55 private Spinner mSyncWindowView; 56 private CheckBox mDefaultView; 57 private CheckBox mNotifyView; 58 private CheckBox mSyncContactsView; 59 private CheckBox mSyncCalendarView; 60 private CheckBox mSyncEmailView; 61 private boolean mDonePressed = false; 62 63 public static final int REQUEST_CODE_ACCEPT_POLICIES = 1; 64 65 /** Default sync window for new EAS accounts */ 66 private static final int SYNC_WINDOW_EAS_DEFAULT = com.android.email.Account.SYNC_WINDOW_3_DAYS; 67 68 public static void actionOptions(Activity fromActivity) { 69 fromActivity.startActivity(new Intent(fromActivity, AccountSetupOptions.class)); 70 } 71 72 @Override 73 public void onCreate(Bundle savedInstanceState) { 74 super.onCreate(savedInstanceState); 75 ActivityHelper.debugSetWindowFlags(this); 76 setContentView(R.layout.account_setup_options); 77 78 mCheckFrequencyView = (Spinner)findViewById(R.id.account_check_frequency); 79 mSyncWindowView = (Spinner) findViewById(R.id.account_sync_window); 80 mDefaultView = (CheckBox)findViewById(R.id.account_default); 81 mNotifyView = (CheckBox)findViewById(R.id.account_notify); 82 mSyncContactsView = (CheckBox) findViewById(R.id.account_sync_contacts); 83 mSyncCalendarView = (CheckBox) findViewById(R.id.account_sync_calendar); 84 mSyncEmailView = (CheckBox) findViewById(R.id.account_sync_email); 85 mSyncEmailView.setChecked(true); 86 findViewById(R.id.previous).setOnClickListener(this); 87 findViewById(R.id.next).setOnClickListener(this); 88 89 // Generate spinner entries using XML arrays used by the preferences 90 int frequencyValuesId; 91 int frequencyEntriesId; 92 Account account = SetupData.getAccount(); 93 Store.StoreInfo info = Store.StoreInfo.getStoreInfo(account.getStoreUri(this), this); 94 if (info.mPushSupported) { 95 frequencyValuesId = R.array.account_settings_check_frequency_values_push; 96 frequencyEntriesId = R.array.account_settings_check_frequency_entries_push; 97 } else { 98 frequencyValuesId = R.array.account_settings_check_frequency_values; 99 frequencyEntriesId = R.array.account_settings_check_frequency_entries; 100 } 101 CharSequence[] frequencyValues = getResources().getTextArray(frequencyValuesId); 102 CharSequence[] frequencyEntries = getResources().getTextArray(frequencyEntriesId); 103 104 // Now create the array used by the Spinner 105 SpinnerOption[] checkFrequencies = new SpinnerOption[frequencyEntries.length]; 106 for (int i = 0; i < frequencyEntries.length; i++) { 107 checkFrequencies[i] = new SpinnerOption( 108 Integer.valueOf(frequencyValues[i].toString()), frequencyEntries[i].toString()); 109 } 110 111 ArrayAdapter<SpinnerOption> checkFrequenciesAdapter = new ArrayAdapter<SpinnerOption>(this, 112 android.R.layout.simple_spinner_item, checkFrequencies); 113 checkFrequenciesAdapter 114 .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 115 mCheckFrequencyView.setAdapter(checkFrequenciesAdapter); 116 117 if (info.mVisibleLimitDefault == -1) { 118 enableEASSyncWindowSpinner(); 119 } 120 121 // Note: It is OK to use mAccount.mIsDefault here *only* because the account 122 // has not been written to the DB yet. Ordinarily, call Account.getDefaultAccountId(). 123 if (account.mIsDefault || SetupData.isDefault()) { 124 mDefaultView.setChecked(true); 125 } 126 mNotifyView.setChecked( 127 (account.getFlags() & EmailContent.Account.FLAGS_NOTIFY_NEW_MAIL) != 0); 128 SpinnerOption.setSpinnerOptionValue(mCheckFrequencyView, account.getSyncInterval()); 129 130 // Setup any additional items to support EAS & EAS flow mode 131 if ("eas".equals(info.mScheme)) { 132 // "also sync contacts" == "true" 133 mSyncContactsView.setVisibility(View.VISIBLE); 134 mSyncContactsView.setChecked(true); 135 mSyncCalendarView.setVisibility(View.VISIBLE); 136 mSyncCalendarView.setChecked(true); 137 // Show the associated dividers 138 findViewById(R.id.account_sync_contacts_divider).setVisibility(View.VISIBLE); 139 findViewById(R.id.account_sync_calendar_divider).setVisibility(View.VISIBLE); 140 } 141 142 // If we are just visiting here to fill in details, exit immediately 143 if (SetupData.isAutoSetup() || 144 SetupData.getFlowMode() == SetupData.FLOW_MODE_FORCE_CREATE) { 145 onDone(); 146 } 147 } 148 149 /** 150 * Respond to clicks in the "Next" or "Previous" buttons 151 */ 152 @Override 153 public void onClick(View view) { 154 switch (view.getId()) { 155 case R.id.next: 156 // Don't allow this more than once (Exchange accounts call an async method 157 // before finish()'ing the Activity, which allows this code to potentially be 158 // executed multiple times 159 if (!mDonePressed) { 160 onDone(); 161 mDonePressed = true; 162 } 163 break; 164 case R.id.previous: 165 onBackPressed(); 166 break; 167 } 168 } 169 170 /** 171 * Ths is called when the user clicks the "done" button. 172 * It collects the data from the UI, updates the setup account record, and commits 173 * the account to the database (making it real for the first time.) 174 * Finally, we call setupAccountManagerAccount(), which will eventually complete via callback. 175 */ 176 private void onDone() { 177 final Account account = SetupData.getAccount(); 178 account.setDisplayName(account.getEmailAddress()); 179 int newFlags = account.getFlags() & ~(EmailContent.Account.FLAGS_NOTIFY_NEW_MAIL); 180 if (mNotifyView.isChecked()) { 181 newFlags |= EmailContent.Account.FLAGS_NOTIFY_NEW_MAIL; 182 } 183 account.setFlags(newFlags); 184 account.setSyncInterval((Integer)((SpinnerOption)mCheckFrequencyView 185 .getSelectedItem()).value); 186 if (findViewById(R.id.account_sync_window_row).getVisibility() == View.VISIBLE) { 187 int window = (Integer)((SpinnerOption)mSyncWindowView.getSelectedItem()).value; 188 account.setSyncLookback(window); 189 } 190 account.setDefaultAccount(mDefaultView.isChecked()); 191 192 if (account.isSaved()) { 193 throw new IllegalStateException("in AccountSetupOptions with already-saved account"); 194 } 195 if (account.mHostAuthRecv == null) { 196 throw new IllegalStateException("in AccountSetupOptions with null mHostAuthRecv"); 197 } 198 199 // Finish setting up the account, and commit it to the database 200 // Set the incomplete flag here to avoid reconciliation issues in ExchangeService 201 account.mFlags |= Account.FLAGS_INCOMPLETE; 202 boolean calendar = false; 203 boolean contacts = false; 204 boolean email = mSyncEmailView.isChecked(); 205 if (account.mHostAuthRecv.mProtocol.equals("eas")) { 206 // Set security hold if necessary to prevent sync until policies are accepted 207 PolicySet policySet = SetupData.getPolicySet(); 208 if (policySet != null && policySet.getSecurityCode() != 0) { 209 account.mSecurityFlags = policySet.getSecurityCode(); 210 account.mFlags |= Account.FLAGS_SECURITY_HOLD; 211 } 212 // Get flags for contacts/calendar sync 213 contacts = mSyncContactsView.isChecked(); 214 calendar = mSyncCalendarView.isChecked(); 215 } 216 217 // Finally, write the completed account (for the first time) and then 218 // install it into the Account manager as well. These are done off-thread. 219 // The account manager will report back via the callback, which will take us to 220 // the next operations. 221 final boolean email2 = email; 222 final boolean calendar2 = calendar; 223 final boolean contacts2 = contacts; 224 Utility.runAsync(new Runnable() { 225 @Override 226 public void run() { 227 Context context = AccountSetupOptions.this; 228 AccountSettingsUtils.commitSettings(context, account); 229 MailService.setupAccountManagerAccount(context, account, 230 email2, calendar2, contacts2, mAccountManagerCallback); 231 } 232 }); 233 } 234 235 /** 236 * This is called at the completion of MailService.setupAccountManagerAccount() 237 */ 238 AccountManagerCallback<Bundle> mAccountManagerCallback = new AccountManagerCallback<Bundle>() { 239 public void run(AccountManagerFuture<Bundle> future) { 240 try { 241 Bundle bundle = future.getResult(); 242 bundle.keySet(); 243 AccountSetupOptions.this.runOnUiThread(new Runnable() { 244 public void run() { 245 optionsComplete(); 246 } 247 }); 248 return; 249 } catch (OperationCanceledException e) { 250 Log.d(Email.LOG_TAG, "addAccount was canceled"); 251 } catch (IOException e) { 252 Log.d(Email.LOG_TAG, "addAccount failed: " + e); 253 } catch (AuthenticatorException e) { 254 Log.d(Email.LOG_TAG, "addAccount failed: " + e); 255 } 256 showErrorDialog(R.string.account_setup_failed_dlg_auth_message, 257 R.string.system_account_create_failed); 258 } 259 }; 260 261 /** 262 * This is called if MailService.setupAccountManagerAccount() fails for some reason 263 */ 264 private void showErrorDialog(final int msgResId, final Object... args) { 265 runOnUiThread(new Runnable() { 266 public void run() { 267 new AlertDialog.Builder(AccountSetupOptions.this) 268 .setIcon(android.R.drawable.ic_dialog_alert) 269 .setTitle(getString(R.string.account_setup_failed_dlg_title)) 270 .setMessage(getString(msgResId, args)) 271 .setCancelable(true) 272 .setPositiveButton( 273 getString(R.string.account_setup_failed_dlg_edit_details_action), 274 new DialogInterface.OnClickListener() { 275 public void onClick(DialogInterface dialog, int which) { 276 finish(); 277 } 278 }) 279 .show(); 280 } 281 }); 282 } 283 284 /** 285 * This is called after the account manager creates the new account. 286 */ 287 private void optionsComplete() { 288 // If we've got policies for this account, ask the user to accept. 289 Account account = SetupData.getAccount(); 290 if ((account.mFlags & Account.FLAGS_SECURITY_HOLD) != 0) { 291 Intent intent = AccountSecurity.actionUpdateSecurityIntent(this, account.mId); 292 startActivityForResult(intent, AccountSetupOptions.REQUEST_CODE_ACCEPT_POLICIES); 293 return; 294 } 295 saveAccountAndFinish(); 296 } 297 298 /** 299 * This is called after the AccountSecurity activity completes. 300 */ 301 @Override 302 public void onActivityResult(int requestCode, int resultCode, Intent data) { 303 saveAccountAndFinish(); 304 } 305 306 /** 307 * These are the final cleanup steps when creating an account: 308 * Clear incomplete & security hold flags 309 * Update account in DB 310 * Enable email services 311 * Enable exchange services 312 * Move to final setup screen 313 */ 314 private void saveAccountAndFinish() { 315 Utility.runAsync(new Runnable() { 316 @Override 317 public void run() { 318 AccountSetupOptions context = AccountSetupOptions.this; 319 // Clear the incomplete/security hold flag now 320 Account account = SetupData.getAccount(); 321 account.mFlags &= ~(Account.FLAGS_INCOMPLETE | Account.FLAGS_SECURITY_HOLD); 322 AccountSettingsUtils.commitSettings(context, account); 323 // Start up services based on new account(s) 324 Email.setServicesEnabledSync(context); 325 ExchangeUtils.startExchangeService(context); 326 // Move to final setup screen 327 AccountSetupNames.actionSetNames(context); 328 finish(); 329 } 330 }); 331 } 332 333 /** 334 * Enable an additional spinner using the arrays normally handled by preferences 335 */ 336 private void enableEASSyncWindowSpinner() { 337 // Show everything 338 findViewById(R.id.account_sync_window_row).setVisibility(View.VISIBLE); 339 340 // Generate spinner entries using XML arrays used by the preferences 341 CharSequence[] windowValues = getResources().getTextArray( 342 R.array.account_settings_mail_window_values); 343 CharSequence[] windowEntries = getResources().getTextArray( 344 R.array.account_settings_mail_window_entries); 345 346 // Now create the array used by the Spinner 347 SpinnerOption[] windowOptions = new SpinnerOption[windowEntries.length]; 348 int defaultIndex = -1; 349 for (int i = 0; i < windowEntries.length; i++) { 350 final int value = Integer.valueOf(windowValues[i].toString()); 351 windowOptions[i] = new SpinnerOption(value, windowEntries[i].toString()); 352 if (value == SYNC_WINDOW_EAS_DEFAULT) { 353 defaultIndex = i; 354 } 355 } 356 357 ArrayAdapter<SpinnerOption> windowOptionsAdapter = new ArrayAdapter<SpinnerOption>(this, 358 android.R.layout.simple_spinner_item, windowOptions); 359 windowOptionsAdapter 360 .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 361 mSyncWindowView.setAdapter(windowOptionsAdapter); 362 363 SpinnerOption.setSpinnerOptionValue(mSyncWindowView, 364 SetupData.getAccount().getSyncLookback()); 365 if (defaultIndex >= 0) { 366 mSyncWindowView.setSelection(defaultIndex); 367 } 368 } 369} 370