AccountSetupOptions.java revision f419287f22ae44f25e1ba1f757ec33c7941bbfa8
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 android.accounts.AccountAuthenticatorResponse; 20import android.accounts.AccountManager; 21import android.accounts.AccountManagerCallback; 22import android.accounts.AccountManagerFuture; 23import android.accounts.AuthenticatorException; 24import android.accounts.OperationCanceledException; 25import android.app.Activity; 26import android.app.AlertDialog; 27import android.content.Context; 28import android.content.DialogInterface; 29import android.content.Intent; 30import android.os.Bundle; 31import android.util.Log; 32import android.view.View; 33import android.view.View.OnClickListener; 34import android.widget.ArrayAdapter; 35import android.widget.CheckBox; 36import android.widget.Spinner; 37 38import com.android.email.R; 39import com.android.email.activity.ActivityHelper; 40import com.android.email.activity.UiUtilities; 41import com.android.email.service.EmailServiceUtils; 42import com.android.email.service.EmailServiceUtils.EmailServiceInfo; 43import com.android.email.service.MailService; 44import com.android.email2.ui.MailActivityEmail; 45import com.android.emailcommon.Logging; 46import com.android.emailcommon.provider.Account; 47import com.android.emailcommon.provider.HostAuth; 48import com.android.emailcommon.provider.Policy; 49import com.android.emailcommon.service.SyncWindow; 50import com.android.emailcommon.utility.Utility; 51 52import java.io.IOException; 53 54public class AccountSetupOptions extends AccountSetupActivity implements OnClickListener { 55 56 private Spinner mCheckFrequencyView; 57 private Spinner mSyncWindowView; 58 private CheckBox mDefaultView; 59 private CheckBox mNotifyView; 60 private CheckBox mSyncContactsView; 61 private CheckBox mSyncCalendarView; 62 private CheckBox mSyncEmailView; 63 private CheckBox mBackgroundAttachmentsView; 64 private View mAccountSyncWindowRow; 65 private boolean mDonePressed = false; 66 private EmailServiceInfo mServiceInfo; 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 ForwardingIntent(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 Account account = SetupData.getAccount(); 99 mServiceInfo = EmailServiceUtils.getServiceInfo(getApplicationContext(), 100 account.mHostAuthRecv.mProtocol); 101 CharSequence[] frequencyValues = mServiceInfo.syncIntervals; 102 CharSequence[] frequencyEntries = mServiceInfo.syncIntervalStrings; 103 104 // Now create the array used by the sync interval 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 ArrayAdapter<SpinnerOption> checkFrequenciesAdapter = new ArrayAdapter<SpinnerOption>(this, 111 android.R.layout.simple_spinner_item, checkFrequencies); 112 checkFrequenciesAdapter 113 .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 114 mCheckFrequencyView.setAdapter(checkFrequenciesAdapter); 115 116 if (mServiceInfo.offerLookback) { 117 enableLookbackSpinner(); 118 } 119 120 // Note: It is OK to use mAccount.mIsDefault here *only* because the account 121 // has not been written to the DB yet. Ordinarily, call Account.getDefaultAccountId(). 122 if (account.mIsDefault || SetupData.isDefault()) { 123 mDefaultView.setChecked(true); 124 } 125 mNotifyView.setChecked( 126 (account.getFlags() & Account.FLAGS_NOTIFY_NEW_MAIL) != 0); 127 SpinnerOption.setSpinnerOptionValue(mCheckFrequencyView, account.getSyncInterval()); 128 129 if (mServiceInfo.syncContacts) { 130 mSyncContactsView.setVisibility(View.VISIBLE); 131 mSyncContactsView.setChecked(true); 132 UiUtilities.setVisibilitySafe(this, R.id.account_sync_contacts_divider, View.VISIBLE); 133 } 134 if (mServiceInfo.syncCalendar) { 135 mSyncCalendarView.setVisibility(View.VISIBLE); 136 mSyncCalendarView.setChecked(true); 137 UiUtilities.setVisibilitySafe(this, R.id.account_sync_calendar_divider, View.VISIBLE); 138 } 139 140 if (!mServiceInfo.offerAttachmentPreload) { 141 mBackgroundAttachmentsView.setVisibility(View.GONE); 142 UiUtilities.setVisibilitySafe(this, R.id.account_background_attachments_divider, 143 View.GONE); 144 } 145 146 // If we are just visiting here to fill in details, exit immediately 147 if (SetupData.isAutoSetup() || 148 SetupData.getFlowMode() == SetupData.FLOW_MODE_FORCE_CREATE) { 149 onDone(); 150 } 151 } 152 153 @Override 154 public void finish() { 155 // If the account manager initiated the creation, and success was not reported, 156 // then we assume that we're giving up (for any reason) - report failure. 157 AccountAuthenticatorResponse authenticatorResponse = 158 SetupData.getAccountAuthenticatorResponse(); 159 if (authenticatorResponse != null) { 160 authenticatorResponse.onError(AccountManager.ERROR_CODE_CANCELED, "canceled"); 161 SetupData.setAccountAuthenticatorResponse(null); 162 } 163 super.finish(); 164 } 165 166 /** 167 * Respond to clicks in the "Next" or "Previous" buttons 168 */ 169 @Override 170 public void onClick(View view) { 171 switch (view.getId()) { 172 case R.id.next: 173 // Don't allow this more than once (Exchange accounts call an async method 174 // before finish()'ing the Activity, which allows this code to potentially be 175 // executed multiple times 176 if (!mDonePressed) { 177 onDone(); 178 mDonePressed = true; 179 } 180 break; 181 case R.id.previous: 182 onBackPressed(); 183 break; 184 } 185 } 186 187 /** 188 * Ths is called when the user clicks the "done" button. 189 * It collects the data from the UI, updates the setup account record, and commits 190 * the account to the database (making it real for the first time.) 191 * Finally, we call setupAccountManagerAccount(), which will eventually complete via callback. 192 */ 193 @SuppressWarnings("deprecation") 194 private void onDone() { 195 final Account account = SetupData.getAccount(); 196 if (account.isSaved()) { 197 // Disrupting the normal flow could get us here, but if the account is already 198 // saved, we've done this work 199 return; 200 } 201 account.setDisplayName(account.getEmailAddress()); 202 int newFlags = account.getFlags() & 203 ~(Account.FLAGS_NOTIFY_NEW_MAIL | Account.FLAGS_BACKGROUND_ATTACHMENTS); 204 if (mNotifyView.isChecked()) { 205 newFlags |= Account.FLAGS_NOTIFY_NEW_MAIL; 206 } 207 if (mBackgroundAttachmentsView.isChecked()) { 208 newFlags |= Account.FLAGS_BACKGROUND_ATTACHMENTS; 209 } 210 account.setFlags(newFlags); 211 account.setSyncInterval((Integer)((SpinnerOption)mCheckFrequencyView 212 .getSelectedItem()).value); 213 if (mAccountSyncWindowRow.getVisibility() == View.VISIBLE) { 214 int window = (Integer)((SpinnerOption)mSyncWindowView.getSelectedItem()).value; 215 account.setSyncLookback(window); 216 } 217 account.setDefaultAccount(mDefaultView.isChecked()); 218 219 if (account.mHostAuthRecv == null) { 220 throw new IllegalStateException("in AccountSetupOptions with null mHostAuthRecv"); 221 } 222 223 // Finish setting up the account, and commit it to the database 224 // Set the incomplete flag here to avoid reconciliation issues in ExchangeService 225 account.mFlags |= Account.FLAGS_INCOMPLETE; 226 if (SetupData.getPolicy() != null) { 227 account.mFlags |= Account.FLAGS_SECURITY_HOLD; 228 account.mPolicy = SetupData.getPolicy(); 229 } 230 boolean contacts = false; 231 if (mServiceInfo.syncContacts) { 232 contacts = mSyncContactsView.isChecked(); 233 } 234 boolean calendar = false; 235 if (mServiceInfo.syncCalendar) { 236 calendar = mSyncCalendarView.isChecked(); 237 } 238 boolean email = mSyncEmailView.isChecked(); 239 240 // Finally, write the completed account (for the first time) and then 241 // install it into the Account manager as well. These are done off-thread. 242 // The account manager will report back via the callback, which will take us to 243 // the next operations. 244 final boolean email2 = email; 245 final boolean calendar2 = calendar; 246 final boolean contacts2 = contacts; 247 Utility.runAsync(new Runnable() { 248 @Override 249 public void run() { 250 Context context = AccountSetupOptions.this; 251 AccountSettingsUtils.commitSettings(context, account); 252 MailService.setupAccountManagerAccount(context, account, 253 email2, calendar2, contacts2, mAccountManagerCallback); 254 } 255 }); 256 } 257 258 /** 259 * This is called at the completion of MailService.setupAccountManagerAccount() 260 */ 261 AccountManagerCallback<Bundle> mAccountManagerCallback = new AccountManagerCallback<Bundle>() { 262 @Override 263 public void run(AccountManagerFuture<Bundle> future) { 264 try { 265 Bundle bundle = future.getResult(); 266 bundle.keySet(); 267 AccountSetupOptions.this.runOnUiThread(new Runnable() { 268 @Override 269 public void run() { 270 optionsComplete(); 271 } 272 }); 273 return; 274 } catch (OperationCanceledException e) { 275 Log.d(Logging.LOG_TAG, "addAccount was canceled"); 276 } catch (IOException e) { 277 Log.d(Logging.LOG_TAG, "addAccount failed: " + e); 278 } catch (AuthenticatorException e) { 279 Log.d(Logging.LOG_TAG, "addAccount failed: " + e); 280 } 281 showErrorDialog(R.string.account_setup_failed_dlg_auth_message, 282 R.string.system_account_create_failed); 283 } 284 }; 285 286 /** 287 * This is called if MailService.setupAccountManagerAccount() fails for some reason 288 */ 289 private void showErrorDialog(final int msgResId, final Object... args) { 290 runOnUiThread(new Runnable() { 291 @Override 292 public void run() { 293 new AlertDialog.Builder(AccountSetupOptions.this) 294 .setIconAttribute(android.R.attr.alertDialogIcon) 295 .setTitle(getString(R.string.account_setup_failed_dlg_title)) 296 .setMessage(getString(msgResId, args)) 297 .setCancelable(true) 298 .setPositiveButton( 299 getString(R.string.account_setup_failed_dlg_edit_details_action), 300 new DialogInterface.OnClickListener() { 301 @Override 302 public void onClick(DialogInterface dialog, int which) { 303 finish(); 304 } 305 }) 306 .show(); 307 } 308 }); 309 } 310 311 /** 312 * This is called after the account manager creates the new account. 313 */ 314 private void optionsComplete() { 315 // If the account manager initiated the creation, report success at this point 316 AccountAuthenticatorResponse authenticatorResponse = 317 SetupData.getAccountAuthenticatorResponse(); 318 if (authenticatorResponse != null) { 319 authenticatorResponse.onResult(null); 320 SetupData.setAccountAuthenticatorResponse(null); 321 } 322 323 // Now that AccountManager account creation is complete, clear the INCOMPLETE flag 324 Account account = SetupData.getAccount(); 325 account.mFlags &= ~Account.FLAGS_INCOMPLETE; 326 AccountSettingsUtils.commitSettings(AccountSetupOptions.this, account); 327 328 // If we've got policies for this account, ask the user to accept. 329 if ((account.mFlags & Account.FLAGS_SECURITY_HOLD) != 0) { 330 Intent intent = AccountSecurity.actionUpdateSecurityIntent(this, account.mId, false); 331 startActivityForResult(intent, AccountSetupOptions.REQUEST_CODE_ACCEPT_POLICIES); 332 return; 333 } 334 saveAccountAndFinish(); 335 } 336 337 /** 338 * This is called after the AccountSecurity activity completes. 339 */ 340 @Override 341 public void onActivityResult(int requestCode, int resultCode, Intent data) { 342 saveAccountAndFinish(); 343 } 344 345 /** 346 * These are the final cleanup steps when creating an account: 347 * Clear incomplete & security hold flags 348 * Update account in DB 349 * Enable email services 350 * Enable exchange services 351 * Move to final setup screen 352 */ 353 @SuppressWarnings("deprecation") 354 private void saveAccountAndFinish() { 355 Utility.runAsync(new Runnable() { 356 @Override 357 public void run() { 358 AccountSetupOptions context = AccountSetupOptions.this; 359 // Clear the security hold flag now 360 Account account = SetupData.getAccount(); 361 account.mFlags &= ~Account.FLAGS_SECURITY_HOLD; 362 AccountSettingsUtils.commitSettings(context, account); 363 // Start up services based on new account(s) 364 MailActivityEmail.setServicesEnabledSync(context); 365 EmailServiceUtils.startService(context, account.mHostAuthRecv.mProtocol); 366 // Move to final setup screen 367 AccountSetupNames.actionSetNames(context); 368 finish(); 369 } 370 }); 371 } 372 373 /** 374 * Enable an additional spinner using the arrays normally handled by preferences 375 */ 376 private void enableLookbackSpinner() { 377 // Show everything 378 mAccountSyncWindowRow.setVisibility(View.VISIBLE); 379 380 // Generate spinner entries using XML arrays used by the preferences 381 CharSequence[] windowValues = getResources().getTextArray( 382 R.array.account_settings_mail_window_values); 383 CharSequence[] windowEntries = getResources().getTextArray( 384 R.array.account_settings_mail_window_entries); 385 386 // Find a proper maximum for email lookback, based on policy (if we have one) 387 int maxEntry = windowEntries.length; 388 Policy policy = SetupData.getAccount().mPolicy; 389 if (policy != null) { 390 int maxLookback = policy.mMaxEmailLookback; 391 if (maxLookback != 0) { 392 // Offset/Code 0 1 2 3 4 5 393 // Entries auto, 1 day, 3 day, 1 week, 2 week, 1 month 394 // Lookback N/A 1 day, 3 day, 1 week, 2 week, 1 month 395 // Since our test below is i < maxEntry, we must set maxEntry to maxLookback + 1 396 maxEntry = maxLookback + 1; 397 } 398 } 399 400 // Now create the array used by the Spinner 401 SpinnerOption[] windowOptions = new SpinnerOption[maxEntry]; 402 int defaultIndex = -1; 403 for (int i = 0; i < maxEntry; i++) { 404 final int value = Integer.valueOf(windowValues[i].toString()); 405 windowOptions[i] = new SpinnerOption(value, windowEntries[i].toString()); 406 if (value == SYNC_WINDOW_EAS_DEFAULT) { 407 defaultIndex = i; 408 } 409 } 410 411 ArrayAdapter<SpinnerOption> windowOptionsAdapter = new ArrayAdapter<SpinnerOption>(this, 412 android.R.layout.simple_spinner_item, windowOptions); 413 windowOptionsAdapter 414 .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 415 mSyncWindowView.setAdapter(windowOptionsAdapter); 416 417 SpinnerOption.setSpinnerOptionValue(mSyncWindowView, 418 SetupData.getAccount().getSyncLookback()); 419 if (defaultIndex >= 0) { 420 mSyncWindowView.setSelection(defaultIndex); 421 } 422 } 423} 424