Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2010 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 | |
Andrew Stadler | fd14496 | 2010-09-10 15:50:55 -0700 | [diff] [blame] | 19 | import android.app.Activity; |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 20 | import android.app.AlertDialog; |
| 21 | import android.app.Dialog; |
| 22 | import android.app.DialogFragment; |
| 23 | import android.app.Fragment; |
| 24 | import android.app.FragmentManager; |
| 25 | import android.app.ProgressDialog; |
| 26 | import android.content.Context; |
| 27 | import android.content.DialogInterface; |
| 28 | import android.os.AsyncTask; |
| 29 | import android.os.Bundle; |
Ben Komalo | 5a2d7da | 2011-04-13 16:07:50 -0700 | [diff] [blame] | 30 | import android.text.TextUtils; |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 31 | import android.util.Log; |
| 32 | |
Marc Blank | 35b0e95 | 2011-06-29 12:47:56 -0700 | [diff] [blame] | 33 | import com.android.email.R; |
| 34 | import com.android.email.mail.Sender; |
| 35 | import com.android.email.mail.Store; |
| 36 | import com.android.emailcommon.Logging; |
| 37 | import com.android.emailcommon.mail.MessagingException; |
| 38 | import com.android.emailcommon.provider.Account; |
| 39 | import com.android.emailcommon.provider.HostAuth; |
| 40 | import com.android.emailcommon.provider.Policy; |
| 41 | import com.android.emailcommon.service.EmailServiceProxy; |
| 42 | import com.android.emailcommon.utility.Utility; |
| 43 | |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 44 | /** |
| 45 | * Check incoming or outgoing settings, or perform autodiscovery. |
| 46 | * |
| 47 | * There are three components that work together. 1. This fragment is retained and non-displayed, |
| 48 | * and controls the overall process. 2. An AsyncTask that works with the stores/services to |
| 49 | * check the accounts settings. 3. A stateless progress dialog (which will be recreated on |
| 50 | * orientation changes). |
| 51 | * |
| 52 | * There are also two lightweight error dialogs which are used for notification of terminal |
| 53 | * conditions. |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 54 | */ |
| 55 | public class AccountCheckSettingsFragment extends Fragment { |
Makoto Onuki | 49518bb | 2011-01-18 10:14:14 -0800 | [diff] [blame] | 56 | |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 57 | public final static String TAG = "AccountCheckSettingsFragment"; |
| 58 | |
| 59 | // Debugging flags - for debugging the UI |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 60 | // If true, use a "fake" account check cycle |
| 61 | private static final boolean DEBUG_FAKE_CHECK_CYCLE = false; // DO NOT CHECK IN TRUE |
| 62 | // If true, use fake check cycle, return failure |
| 63 | private static final boolean DEBUG_FAKE_CHECK_ERR = false; // DO NOT CHECK IN TRUE |
| 64 | // If true, use fake check cycle, return "security required" |
| 65 | private static final boolean DEBUG_FORCE_SECURITY_REQUIRED = false; // DO NOT CHECK IN TRUE |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 66 | |
| 67 | // State |
| 68 | private final static int STATE_START = 0; |
| 69 | private final static int STATE_CHECK_AUTODISCOVER = 1; |
| 70 | private final static int STATE_CHECK_INCOMING = 2; |
| 71 | private final static int STATE_CHECK_OUTGOING = 3; |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 72 | private final static int STATE_CHECK_OK = 4; // terminal |
| 73 | private final static int STATE_CHECK_SHOW_SECURITY = 5; // terminal |
| 74 | private final static int STATE_CHECK_ERROR = 6; // terminal |
| 75 | private final static int STATE_AUTODISCOVER_AUTH_DIALOG = 7; // terminal |
| 76 | private final static int STATE_AUTODISCOVER_RESULT = 8; // terminal |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 77 | private int mState = STATE_START; |
| 78 | |
| 79 | // Support for UI |
| 80 | private boolean mAttached; |
| 81 | private CheckingDialog mCheckingDialog; |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 82 | private MessagingException mProgressException; |
Andrew Stadler | fd14496 | 2010-09-10 15:50:55 -0700 | [diff] [blame] | 83 | |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 84 | // Support for AsyncTask and account checking |
| 85 | AccountCheckTask mAccountCheckTask; |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 86 | |
| 87 | // Result codes returned by onCheckSettingsComplete. |
| 88 | /** Check settings returned successfully */ |
| 89 | public final static int CHECK_SETTINGS_OK = 0; |
| 90 | /** Check settings failed due to connection, authentication, or other server error */ |
| 91 | public final static int CHECK_SETTINGS_SERVER_ERROR = 1; |
| 92 | /** Check settings failed due to user refusing to accept security requirements */ |
| 93 | public final static int CHECK_SETTINGS_SECURITY_USER_DENY = 2; |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 94 | /** Check settings failed due to certificate being required - user needs to pick immediately. */ |
| 95 | public final static int CHECK_SETTINGS_CLIENT_CERTIFICATE_NEEDED = 3; |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 96 | |
| 97 | // Result codes returned by onAutoDiscoverComplete. |
| 98 | /** AutoDiscover completed successfully with server setup data */ |
| 99 | public final static int AUTODISCOVER_OK = 0; |
| 100 | /** AutoDiscover completed with no data (no server or AD not supported) */ |
| 101 | public final static int AUTODISCOVER_NO_DATA = 1; |
| 102 | /** AutoDiscover reported authentication error */ |
| 103 | public final static int AUTODISCOVER_AUTHENTICATION = 2; |
| 104 | |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 105 | /** |
Andrew Stadler | fd14496 | 2010-09-10 15:50:55 -0700 | [diff] [blame] | 106 | * Callback interface for any target or activity doing account check settings |
| 107 | */ |
| 108 | public interface Callbacks { |
| 109 | /** |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 110 | * Called when CheckSettings completed |
| 111 | * @param result check settings result code - success is CHECK_SETTINGS_OK |
Andrew Stadler | fd14496 | 2010-09-10 15:50:55 -0700 | [diff] [blame] | 112 | */ |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 113 | public void onCheckSettingsComplete(int result); |
| 114 | |
| 115 | /** |
| 116 | * Called when autodiscovery completes. |
| 117 | * @param result autodiscovery result code - success is AUTODISCOVER_OK |
| 118 | * @param hostAuth configuration data returned by AD server, or null if no data available |
| 119 | */ |
| 120 | public void onAutoDiscoverComplete(int result, HostAuth hostAuth); |
Andrew Stadler | fd14496 | 2010-09-10 15:50:55 -0700 | [diff] [blame] | 121 | } |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 122 | |
Andrew Stadler | fd14496 | 2010-09-10 15:50:55 -0700 | [diff] [blame] | 123 | /** |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 124 | * Create a retained, invisible fragment that checks accounts |
| 125 | * |
| 126 | * @param mode incoming or outgoing |
| 127 | */ |
| 128 | public static AccountCheckSettingsFragment newInstance(int mode, Fragment parentFragment) { |
| 129 | AccountCheckSettingsFragment f = new AccountCheckSettingsFragment(); |
| 130 | f.setTargetFragment(parentFragment, mode); |
| 131 | return f; |
| 132 | } |
| 133 | |
| 134 | /** |
| 135 | * Fragment initialization. Because we never implement onCreateView, and call |
| 136 | * setRetainInstance here, this creates an invisible, persistent, "worker" fragment. |
| 137 | */ |
| 138 | @Override |
| 139 | public void onCreate(Bundle savedInstanceState) { |
| 140 | super.onCreate(savedInstanceState); |
| 141 | setRetainInstance(true); |
| 142 | } |
| 143 | |
| 144 | /** |
| 145 | * This is called when the Fragment's Activity is ready to go, after |
| 146 | * its content view has been installed; it is called both after |
| 147 | * the initial fragment creation and after the fragment is re-attached |
| 148 | * to a new activity. |
| 149 | */ |
| 150 | @Override |
| 151 | public void onActivityCreated(Bundle savedInstanceState) { |
| 152 | super.onActivityCreated(savedInstanceState); |
| 153 | mAttached = true; |
| 154 | |
| 155 | // If this is the first time, start the AsyncTask |
| 156 | if (mAccountCheckTask == null) { |
| 157 | int checkMode = getTargetRequestCode(); |
| 158 | Account checkAccount = SetupData.getAccount(); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 159 | mAccountCheckTask = (AccountCheckTask) |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 160 | new AccountCheckTask(checkMode, checkAccount) |
Makoto Onuki | bc2eaad | 2011-06-30 16:10:06 -0700 | [diff] [blame] | 161 | .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 162 | } |
Andy Stadler | f946ff0 | 2010-12-13 13:58:40 -0800 | [diff] [blame] | 163 | } |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 164 | |
Andy Stadler | f946ff0 | 2010-12-13 13:58:40 -0800 | [diff] [blame] | 165 | /** |
| 166 | * When resuming, restart the progress/error UI if necessary by re-reporting previous values |
| 167 | */ |
| 168 | @Override |
| 169 | public void onResume() { |
| 170 | super.onResume(); |
| 171 | |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 172 | if (mState != STATE_START) { |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 173 | reportProgress(mState, mProgressException); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 174 | } |
| 175 | } |
| 176 | |
| 177 | /** |
| 178 | * This is called when the fragment is going away. It is NOT called |
| 179 | * when the fragment is being propagated between activity instances. |
| 180 | */ |
| 181 | @Override |
| 182 | public void onDestroy() { |
| 183 | super.onDestroy(); |
| 184 | Utility.cancelTaskInterrupt(mAccountCheckTask); |
| 185 | mAccountCheckTask = null; |
| 186 | } |
| 187 | |
| 188 | /** |
| 189 | * This is called right before the fragment is detached from its current activity instance. |
| 190 | * All reporting and callbacks are halted until we reattach. |
| 191 | */ |
| 192 | @Override |
| 193 | public void onDetach() { |
| 194 | super.onDetach(); |
| 195 | mAttached = false; |
| 196 | } |
| 197 | |
| 198 | /** |
| 199 | * The worker (AsyncTask) will call this (in the UI thread) to report progress. If we are |
| 200 | * attached to an activity, update the progress immediately; If not, simply hold the |
| 201 | * progress for later. |
| 202 | * @param newState The new progress state being reported |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 203 | */ |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 204 | private void reportProgress(int newState, MessagingException ex) { |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 205 | mState = newState; |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 206 | mProgressException = ex; |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 207 | |
| 208 | // If we are attached, create, recover, and/or update the dialog |
| 209 | if (mAttached) { |
| 210 | FragmentManager fm = getFragmentManager(); |
| 211 | |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 212 | switch (newState) { |
| 213 | case STATE_CHECK_OK: |
| 214 | // immediately terminate, clean up, and report back |
| 215 | // 1. get rid of progress dialog (if any) |
| 216 | recoverAndDismissCheckingDialog(); |
| 217 | // 2. exit self |
| 218 | fm.popBackStack(); |
| 219 | // 3. report OK back to target fragment or activity |
| 220 | getCallbackTarget().onCheckSettingsComplete(CHECK_SETTINGS_OK); |
| 221 | break; |
| 222 | case STATE_CHECK_SHOW_SECURITY: |
| 223 | // 1. get rid of progress dialog (if any) |
| 224 | recoverAndDismissCheckingDialog(); |
Andy Stadler | f946ff0 | 2010-12-13 13:58:40 -0800 | [diff] [blame] | 225 | // 2. launch the error dialog, if needed |
| 226 | if (fm.findFragmentByTag(SecurityRequiredDialog.TAG) == null) { |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 227 | String message = ex.getMessage(); |
| 228 | if (message != null) { |
| 229 | message = message.trim(); |
| 230 | } |
Andy Stadler | f946ff0 | 2010-12-13 13:58:40 -0800 | [diff] [blame] | 231 | SecurityRequiredDialog securityRequiredDialog = |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 232 | SecurityRequiredDialog.newInstance(this, message); |
Dianne Hackborn | 31e2576 | 2011-01-17 12:30:51 -0800 | [diff] [blame] | 233 | fm.beginTransaction() |
Andy Stadler | f946ff0 | 2010-12-13 13:58:40 -0800 | [diff] [blame] | 234 | .add(securityRequiredDialog, SecurityRequiredDialog.TAG) |
| 235 | .commit(); |
| 236 | } |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 237 | break; |
| 238 | case STATE_CHECK_ERROR: |
| 239 | case STATE_AUTODISCOVER_AUTH_DIALOG: |
| 240 | // 1. get rid of progress dialog (if any) |
| 241 | recoverAndDismissCheckingDialog(); |
Andy Stadler | f946ff0 | 2010-12-13 13:58:40 -0800 | [diff] [blame] | 242 | // 2. launch the error dialog, if needed |
| 243 | if (fm.findFragmentByTag(ErrorDialog.TAG) == null) { |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 244 | ErrorDialog errorDialog = ErrorDialog.newInstance( |
| 245 | getActivity(), this, mProgressException); |
Dianne Hackborn | 31e2576 | 2011-01-17 12:30:51 -0800 | [diff] [blame] | 246 | fm.beginTransaction() |
Andy Stadler | f946ff0 | 2010-12-13 13:58:40 -0800 | [diff] [blame] | 247 | .add(errorDialog, ErrorDialog.TAG) |
| 248 | .commit(); |
| 249 | } |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 250 | break; |
| 251 | case STATE_AUTODISCOVER_RESULT: |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 252 | HostAuth autoDiscoverResult = ((AutoDiscoverResults) ex).mHostAuth; |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 253 | // 1. get rid of progress dialog (if any) |
| 254 | recoverAndDismissCheckingDialog(); |
| 255 | // 2. exit self |
| 256 | fm.popBackStack(); |
| 257 | // 3. report back to target fragment or activity |
| 258 | getCallbackTarget().onAutoDiscoverComplete( |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 259 | (autoDiscoverResult != null) ? AUTODISCOVER_OK : AUTODISCOVER_NO_DATA, |
| 260 | autoDiscoverResult); |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 261 | break; |
| 262 | default: |
| 263 | // Display a normal progress message |
| 264 | mCheckingDialog = (CheckingDialog) fm.findFragmentByTag(CheckingDialog.TAG); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 265 | |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 266 | if (mCheckingDialog == null) { |
| 267 | mCheckingDialog = CheckingDialog.newInstance(this, mState); |
Dianne Hackborn | 31e2576 | 2011-01-17 12:30:51 -0800 | [diff] [blame] | 268 | fm.beginTransaction() |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 269 | .add(mCheckingDialog, CheckingDialog.TAG) |
| 270 | .commit(); |
| 271 | } else { |
| 272 | mCheckingDialog.updateProgress(mState); |
| 273 | } |
| 274 | break; |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 275 | } |
| 276 | } |
| 277 | } |
| 278 | |
| 279 | /** |
Andrew Stadler | fd14496 | 2010-09-10 15:50:55 -0700 | [diff] [blame] | 280 | * Find the callback target, either a target fragment or the activity |
| 281 | */ |
| 282 | private Callbacks getCallbackTarget() { |
| 283 | Fragment target = getTargetFragment(); |
| 284 | if (target instanceof Callbacks) { |
| 285 | return (Callbacks) target; |
| 286 | } |
| 287 | Activity activity = getActivity(); |
| 288 | if (activity instanceof Callbacks) { |
| 289 | return (Callbacks) activity; |
| 290 | } |
| 291 | throw new IllegalStateException(); |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 292 | } |
Andrew Stadler | fd14496 | 2010-09-10 15:50:55 -0700 | [diff] [blame] | 293 | |
| 294 | /** |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 295 | * Recover and dismiss the progress dialog fragment |
| 296 | */ |
| 297 | private void recoverAndDismissCheckingDialog() { |
| 298 | if (mCheckingDialog == null) { |
| 299 | mCheckingDialog = (CheckingDialog) |
| 300 | getFragmentManager().findFragmentByTag(CheckingDialog.TAG); |
| 301 | } |
| 302 | if (mCheckingDialog != null) { |
Andy Stadler | a685d3b | 2011-03-15 15:02:48 -0700 | [diff] [blame] | 303 | mCheckingDialog.dismissAllowingStateLoss(); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 304 | mCheckingDialog = null; |
| 305 | } |
| 306 | } |
| 307 | |
| 308 | /** |
| 309 | * This is called when the user clicks "cancel" on the progress dialog. Shuts everything |
| 310 | * down and dismisses everything. |
| 311 | * This should cause us to remain in the current screen (not accepting the settings) |
| 312 | */ |
| 313 | private void onCheckingDialogCancel() { |
| 314 | // 1. kill the checker |
| 315 | Utility.cancelTaskInterrupt(mAccountCheckTask); |
| 316 | mAccountCheckTask = null; |
| 317 | // 2. kill self with no report - this is "cancel" |
Ben Komalo | 7683a43 | 2011-09-15 10:56:34 -0700 | [diff] [blame] | 318 | finish(); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 319 | } |
| 320 | |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 321 | private void onEditCertificateOk() { |
| 322 | Callbacks callbackTarget = getCallbackTarget(); |
| 323 | getCallbackTarget().onCheckSettingsComplete(CHECK_SETTINGS_CLIENT_CERTIFICATE_NEEDED); |
| 324 | finish(); |
| 325 | } |
| 326 | |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 327 | /** |
| 328 | * This is called when the user clicks "edit" from the error dialog. The error dialog |
| 329 | * should have already dismissed itself. |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 330 | * Depending on the context, the target will remain in the current activity (e.g. editing |
| 331 | * settings) or return to its own parent (e.g. enter new credentials). |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 332 | */ |
| 333 | private void onErrorDialogEditButton() { |
Andy Stadler | 5f830f8 | 2011-03-10 14:56:10 -0800 | [diff] [blame] | 334 | // 1. handle "edit" - notify callback that we had a problem with the test |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 335 | Callbacks callbackTarget = getCallbackTarget(); |
| 336 | if (mState == STATE_AUTODISCOVER_AUTH_DIALOG) { |
| 337 | // report auth error to target fragment or activity |
| 338 | callbackTarget.onAutoDiscoverComplete(AUTODISCOVER_AUTHENTICATION, null); |
| 339 | } else { |
| 340 | // report check settings failure to target fragment or activity |
| 341 | callbackTarget.onCheckSettingsComplete(CHECK_SETTINGS_SERVER_ERROR); |
| 342 | } |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 343 | finish(); |
| 344 | } |
| 345 | |
| 346 | /** Kill self if not already killed. */ |
| 347 | private void finish() { |
Andy Stadler | 5f830f8 | 2011-03-10 14:56:10 -0800 | [diff] [blame] | 348 | FragmentManager fm = getFragmentManager(); |
| 349 | if (fm != null) { |
| 350 | fm.popBackStack(); |
| 351 | } |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 352 | } |
| 353 | |
| 354 | /** |
| 355 | * This is called when the user clicks "ok" or "cancel" on the "security required" dialog. |
| 356 | * Shuts everything down and dismisses everything, and reports the result appropriately. |
| 357 | */ |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 358 | private void onSecurityRequiredDialogResultOk(boolean okPressed) { |
| 359 | // 1. handle OK/cancel - notify that security is OK and we can proceed |
| 360 | Callbacks callbackTarget = getCallbackTarget(); |
| 361 | callbackTarget.onCheckSettingsComplete( |
| 362 | okPressed ? CHECK_SETTINGS_OK : CHECK_SETTINGS_SECURITY_USER_DENY); |
| 363 | |
Andy Stadler | d8f1d8f | 2010-12-22 19:01:08 -0800 | [diff] [blame] | 364 | // 2. kill self if not already killed by callback |
| 365 | FragmentManager fm = getFragmentManager(); |
| 366 | if (fm != null) { |
| 367 | fm.popBackStack(); |
| 368 | } |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 369 | } |
| 370 | |
| 371 | /** |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 372 | * This exception class is used to report autodiscover results via the reporting mechanism. |
| 373 | */ |
| 374 | public static class AutoDiscoverResults extends MessagingException { |
| 375 | public final HostAuth mHostAuth; |
| 376 | |
| 377 | /** |
| 378 | * @param authenticationError true if auth failure, false for result (or no response) |
| 379 | * @param hostAuth null for "no autodiscover", non-null for server info to return |
| 380 | */ |
| 381 | public AutoDiscoverResults(boolean authenticationError, HostAuth hostAuth) { |
| 382 | super(null); |
| 383 | if (authenticationError) { |
| 384 | mExceptionType = AUTODISCOVER_AUTHENTICATION_FAILED; |
| 385 | } else { |
| 386 | mExceptionType = AUTODISCOVER_AUTHENTICATION_RESULT; |
| 387 | } |
| 388 | mHostAuth = hostAuth; |
| 389 | } |
| 390 | } |
| 391 | |
| 392 | /** |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 393 | * This AsyncTask does the actual account checking |
| 394 | * |
| 395 | * TODO: It would be better to remove the UI complete from here (the exception->string |
| 396 | * conversions). |
| 397 | */ |
| 398 | private class AccountCheckTask extends AsyncTask<Void, Integer, MessagingException> { |
| 399 | |
| 400 | final Context mContext; |
| 401 | final int mMode; |
Todd Kennedy | a50fc99 | 2011-04-19 11:32:06 -0700 | [diff] [blame] | 402 | final Account mAccount; |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 403 | final String mStoreHost; |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 404 | final String mCheckEmail; |
| 405 | final String mCheckPassword; |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 406 | |
| 407 | /** |
| 408 | * Create task and parameterize it |
| 409 | * @param mode bits request operations |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 410 | * @param checkAccount account holding values to be checked |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 411 | */ |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 412 | public AccountCheckTask(int mode, Account checkAccount) { |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 413 | mContext = getActivity().getApplicationContext(); |
| 414 | mMode = mode; |
Todd Kennedy | a50fc99 | 2011-04-19 11:32:06 -0700 | [diff] [blame] | 415 | mAccount = checkAccount; |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 416 | mStoreHost = checkAccount.mHostAuthRecv.mAddress; |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 417 | mCheckEmail = checkAccount.mEmailAddress; |
| 418 | mCheckPassword = checkAccount.mHostAuthRecv.mPassword; |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 419 | } |
| 420 | |
| 421 | @Override |
| 422 | protected MessagingException doInBackground(Void... params) { |
| 423 | if (DEBUG_FAKE_CHECK_CYCLE) { |
| 424 | return fakeChecker(); |
| 425 | } |
| 426 | |
| 427 | try { |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 428 | if ((mMode & SetupData.CHECK_AUTODISCOVER) != 0) { |
| 429 | if (isCancelled()) return null; |
| 430 | publishProgress(STATE_CHECK_AUTODISCOVER); |
Marc Blank | 31d9acb | 2011-02-11 15:05:17 -0800 | [diff] [blame] | 431 | Log.d(Logging.LOG_TAG, "Begin auto-discover for " + mCheckEmail); |
Marc Blank | 35b0e95 | 2011-06-29 12:47:56 -0700 | [diff] [blame] | 432 | Store store = Store.getInstance(mAccount, mContext); |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 433 | Bundle result = store.autoDiscover(mContext, mCheckEmail, mCheckPassword); |
| 434 | // Result will be one of: |
| 435 | // null: remote exception - proceed to manual setup |
| 436 | // MessagingException.AUTHENTICATION_FAILED: username/password rejected |
| 437 | // Other error: proceed to manual setup |
| 438 | // No error: return autodiscover results |
| 439 | if (result == null) { |
| 440 | return new AutoDiscoverResults(false, null); |
| 441 | } |
| 442 | int errorCode = |
| 443 | result.getInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE); |
| 444 | if (errorCode == MessagingException.AUTHENTICATION_FAILED) { |
| 445 | return new AutoDiscoverResults(true, null); |
| 446 | } else if (errorCode != MessagingException.NO_ERROR) { |
| 447 | return new AutoDiscoverResults(false, null); |
| 448 | } else { |
| 449 | HostAuth serverInfo = (HostAuth) |
| 450 | result.getParcelable(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_HOST_AUTH); |
| 451 | return new AutoDiscoverResults(false, serverInfo); |
| 452 | } |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 453 | } |
| 454 | |
| 455 | // Check Incoming Settings |
| 456 | if ((mMode & SetupData.CHECK_INCOMING) != 0) { |
| 457 | if (isCancelled()) return null; |
Marc Blank | 31d9acb | 2011-02-11 15:05:17 -0800 | [diff] [blame] | 458 | Log.d(Logging.LOG_TAG, "Begin check of incoming email settings"); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 459 | publishProgress(STATE_CHECK_INCOMING); |
Marc Blank | 35b0e95 | 2011-06-29 12:47:56 -0700 | [diff] [blame] | 460 | Store store = Store.getInstance(mAccount, mContext); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 461 | Bundle bundle = store.checkSettings(); |
| 462 | int resultCode = MessagingException.UNSPECIFIED_EXCEPTION; |
| 463 | if (bundle != null) { |
| 464 | resultCode = bundle.getInt( |
| 465 | EmailServiceProxy.VALIDATE_BUNDLE_RESULT_CODE); |
| 466 | } |
| 467 | if (resultCode == MessagingException.SECURITY_POLICIES_REQUIRED) { |
Marc Blank | aeee10e | 2011-04-27 17:12:06 -0700 | [diff] [blame] | 468 | SetupData.setPolicy((Policy)bundle.getParcelable( |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 469 | EmailServiceProxy.VALIDATE_BUNDLE_POLICY_SET)); |
Marc Blank | 2655307 | 2011-04-26 12:54:33 -0700 | [diff] [blame] | 470 | return new MessagingException(resultCode, mStoreHost); |
| 471 | } else if (resultCode == MessagingException.SECURITY_POLICIES_UNSUPPORTED) { |
| 472 | String[] data = bundle.getStringArray( |
| 473 | EmailServiceProxy.VALIDATE_BUNDLE_UNSUPPORTED_POLICIES); |
| 474 | return new MessagingException(resultCode, mStoreHost, data); |
| 475 | } else if (resultCode != MessagingException.NO_ERROR) { |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 476 | String errorMessage = |
| 477 | bundle.getString(EmailServiceProxy.VALIDATE_BUNDLE_ERROR_MESSAGE); |
| 478 | return new MessagingException(resultCode, errorMessage); |
| 479 | } |
| 480 | } |
| 481 | |
| 482 | // Check Outgoing Settings |
| 483 | if ((mMode & SetupData.CHECK_OUTGOING) != 0) { |
| 484 | if (isCancelled()) return null; |
Marc Blank | 31d9acb | 2011-02-11 15:05:17 -0800 | [diff] [blame] | 485 | Log.d(Logging.LOG_TAG, "Begin check of outgoing email settings"); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 486 | publishProgress(STATE_CHECK_OUTGOING); |
Todd Kennedy | daf869c | 2011-04-20 08:04:46 -0700 | [diff] [blame] | 487 | Sender sender = Sender.getInstance(mContext, mAccount); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 488 | sender.close(); |
| 489 | sender.open(); |
| 490 | sender.close(); |
| 491 | } |
| 492 | |
| 493 | // If we reached the end, we completed the check(s) successfully |
| 494 | return null; |
| 495 | } catch (final MessagingException me) { |
| 496 | // Some of the legacy account checkers return errors by throwing MessagingException, |
| 497 | // which we catch and return here. |
| 498 | return me; |
| 499 | } |
| 500 | } |
| 501 | |
| 502 | /** |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 503 | * Dummy background worker, for testing UI only. |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 504 | */ |
| 505 | private MessagingException fakeChecker() { |
| 506 | // Dummy: Publish a series of progress setups, 2 sec delays between them; |
| 507 | // then return "ok" (null) |
| 508 | final int DELAY = 2*1000; |
| 509 | if (isCancelled()) return null; |
| 510 | if ((mMode & SetupData.CHECK_AUTODISCOVER) != 0) { |
| 511 | publishProgress(STATE_CHECK_AUTODISCOVER); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 512 | try { |
| 513 | Thread.sleep(DELAY); |
| 514 | } catch (InterruptedException e) { } |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 515 | if (DEBUG_FAKE_CHECK_ERR) { |
| 516 | return new MessagingException(MessagingException.AUTHENTICATION_FAILED); |
| 517 | } |
| 518 | // Return "real" AD results |
Todd Kennedy | 040ddf6 | 2011-02-04 09:29:13 -0800 | [diff] [blame] | 519 | HostAuth auth = new HostAuth(); |
| 520 | auth.setLogin("user", "password"); |
Marc Blank | 6fea021 | 2011-06-29 14:30:03 -0700 | [diff] [blame] | 521 | auth.setConnection(HostAuth.SCHEME_EAS, "testserver.com", 0); |
Todd Kennedy | 040ddf6 | 2011-02-04 09:29:13 -0800 | [diff] [blame] | 522 | return new AutoDiscoverResults(false, auth); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 523 | } |
| 524 | if (isCancelled()) return null; |
| 525 | if ((mMode & SetupData.CHECK_INCOMING) != 0) { |
| 526 | publishProgress(STATE_CHECK_INCOMING); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 527 | try { |
| 528 | Thread.sleep(DELAY); |
| 529 | } catch (InterruptedException e) { } |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 530 | if (DEBUG_FAKE_CHECK_ERR) { |
| 531 | return new MessagingException(MessagingException.IOERROR); |
| 532 | } else if (DEBUG_FORCE_SECURITY_REQUIRED) { |
Marc Blank | 2655307 | 2011-04-26 12:54:33 -0700 | [diff] [blame] | 533 | return new MessagingException(MessagingException.SECURITY_POLICIES_REQUIRED); |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 534 | } |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 535 | } |
| 536 | if (isCancelled()) return null; |
| 537 | if ((mMode & SetupData.CHECK_OUTGOING) != 0) { |
| 538 | publishProgress(STATE_CHECK_OUTGOING); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 539 | try { |
| 540 | Thread.sleep(DELAY); |
| 541 | } catch (InterruptedException e) { } |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 542 | if (DEBUG_FAKE_CHECK_ERR) { |
| 543 | return new MessagingException(MessagingException.TLS_REQUIRED); |
| 544 | } |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 545 | } |
| 546 | return null; |
| 547 | } |
| 548 | |
| 549 | /** |
| 550 | * Progress reports (runs in UI thread). This should be used for real progress only |
| 551 | * (not for errors). |
| 552 | */ |
| 553 | @Override |
| 554 | protected void onProgressUpdate(Integer... progress) { |
| 555 | if (isCancelled()) return; |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 556 | reportProgress(progress[0], null); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 557 | } |
| 558 | |
| 559 | /** |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 560 | * Result handler (runs in UI thread). |
| 561 | * |
| 562 | * AutoDiscover authentication errors are handled a bit differently than the |
| 563 | * other errors; If encountered, we display the error dialog, but we return with |
| 564 | * a different callback used only for AutoDiscover. |
| 565 | * |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 566 | * @param result null for a successful check; exception for various errors |
| 567 | */ |
| 568 | @Override |
| 569 | protected void onPostExecute(MessagingException result) { |
| 570 | if (isCancelled()) return; |
| 571 | if (result == null) { |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 572 | reportProgress(STATE_CHECK_OK, null); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 573 | } else { |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 574 | int progressState = STATE_CHECK_ERROR; |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 575 | int exceptionType = result.getExceptionType(); |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 576 | |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 577 | switch (exceptionType) { |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 578 | // NOTE: AutoDiscover reports have their own reporting state, handle differently |
| 579 | // from the other exception types |
| 580 | case MessagingException.AUTODISCOVER_AUTHENTICATION_FAILED: |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 581 | progressState = STATE_AUTODISCOVER_AUTH_DIALOG; |
| 582 | break; |
| 583 | case MessagingException.AUTODISCOVER_AUTHENTICATION_RESULT: |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 584 | progressState = STATE_AUTODISCOVER_RESULT; |
| 585 | break; |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 586 | // NOTE: Security policies required has its own report state, handle it a bit |
| 587 | // differently from the other exception types. |
| 588 | case MessagingException.SECURITY_POLICIES_REQUIRED: |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 589 | progressState = STATE_CHECK_SHOW_SECURITY; |
| 590 | break; |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 591 | } |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 592 | reportProgress(progressState, result); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 593 | } |
| 594 | } |
| 595 | } |
| 596 | |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 597 | private static String getErrorString(Context context, MessagingException ex) { |
| 598 | int id; |
| 599 | String message = ex.getMessage(); |
| 600 | if (message != null) { |
| 601 | message = message.trim(); |
| 602 | } |
| 603 | switch (ex.getExceptionType()) { |
| 604 | // The remaining exception types are handled by setting the state to |
| 605 | // STATE_CHECK_ERROR (above, default) and conversion to specific error strings. |
| 606 | case MessagingException.CERTIFICATE_VALIDATION_ERROR: |
| 607 | id = TextUtils.isEmpty(message) |
| 608 | ? R.string.account_setup_failed_dlg_certificate_message |
| 609 | : R.string.account_setup_failed_dlg_certificate_message_fmt; |
| 610 | break; |
| 611 | case MessagingException.AUTHENTICATION_FAILED: |
| 612 | id = TextUtils.isEmpty(message) |
| 613 | ? R.string.account_setup_failed_dlg_auth_message |
| 614 | : R.string.account_setup_failed_dlg_auth_message_fmt; |
| 615 | break; |
| 616 | case MessagingException.AUTHENTICATION_FAILED_OR_SERVER_ERROR: |
| 617 | id = R.string.account_setup_failed_check_credentials_message; |
| 618 | break; |
| 619 | case MessagingException.IOERROR: |
| 620 | id = R.string.account_setup_failed_ioerror; |
| 621 | break; |
| 622 | case MessagingException.TLS_REQUIRED: |
| 623 | id = R.string.account_setup_failed_tls_required; |
| 624 | break; |
| 625 | case MessagingException.AUTH_REQUIRED: |
| 626 | id = R.string.account_setup_failed_auth_required; |
| 627 | break; |
| 628 | case MessagingException.SECURITY_POLICIES_UNSUPPORTED: |
| 629 | id = R.string.account_setup_failed_security_policies_unsupported; |
| 630 | // Belt and suspenders here; there should always be a non-empty array here |
| 631 | String[] unsupportedPolicies = (String[]) ex.getExceptionData(); |
| 632 | if (unsupportedPolicies == null) { |
| 633 | Log.w(TAG, "No data for unsupported policies?"); |
| 634 | break; |
| 635 | } |
| 636 | // Build a string, concatenating policies we don't support |
| 637 | StringBuilder sb = new StringBuilder(); |
| 638 | boolean first = true; |
| 639 | for (String policyName: unsupportedPolicies) { |
| 640 | if (first) { |
| 641 | first = false; |
| 642 | } else { |
| 643 | sb.append(", "); |
| 644 | } |
| 645 | sb.append(policyName); |
| 646 | } |
| 647 | message = sb.toString(); |
| 648 | break; |
| 649 | case MessagingException.ACCESS_DENIED: |
| 650 | id = R.string.account_setup_failed_access_denied; |
| 651 | break; |
| 652 | case MessagingException.PROTOCOL_VERSION_UNSUPPORTED: |
| 653 | id = R.string.account_setup_failed_protocol_unsupported; |
| 654 | break; |
| 655 | case MessagingException.GENERAL_SECURITY: |
| 656 | id = R.string.account_setup_failed_security; |
| 657 | break; |
| 658 | case MessagingException.CLIENT_CERTIFICATE_REQUIRED: |
| 659 | id = R.string.account_setup_failed_certificate_required; |
| 660 | break; |
| 661 | case MessagingException.CLIENT_CERTIFICATE_ERROR: |
| 662 | id = R.string.account_setup_failed_certificate_inaccessible; |
| 663 | break; |
| 664 | default: |
| 665 | id = TextUtils.isEmpty(message) |
| 666 | ? R.string.account_setup_failed_dlg_server_message |
| 667 | : R.string.account_setup_failed_dlg_server_message_fmt; |
| 668 | break; |
| 669 | } |
| 670 | return TextUtils.isEmpty(message) |
| 671 | ? context.getString(id) |
| 672 | : context.getString(id, message); |
| 673 | } |
| 674 | |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 675 | /** |
| 676 | * Simple dialog that shows progress as we work through the settings checks. |
| 677 | * This is stateless except for its UI (e.g. current strings) and can be torn down or |
| 678 | * recreated at any time without affecting the account checking progress. |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 679 | */ |
| 680 | public static class CheckingDialog extends DialogFragment { |
Todd Kennedy | 040ddf6 | 2011-02-04 09:29:13 -0800 | [diff] [blame] | 681 | @SuppressWarnings("hiding") |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 682 | public final static String TAG = "CheckProgressDialog"; |
| 683 | |
| 684 | // Extras for saved instance state |
| 685 | private final String EXTRA_PROGRESS_STRING = "CheckProgressDialog.Progress"; |
| 686 | |
| 687 | // UI |
| 688 | private String mProgressString; |
| 689 | |
| 690 | /** |
| 691 | * Create a dialog that reports progress |
| 692 | * @param progress initial progress indication |
| 693 | */ |
| 694 | public static CheckingDialog newInstance(AccountCheckSettingsFragment parentFragment, |
| 695 | int progress) { |
| 696 | CheckingDialog f = new CheckingDialog(); |
| 697 | f.setTargetFragment(parentFragment, progress); |
| 698 | return f; |
| 699 | } |
| 700 | |
| 701 | /** |
| 702 | * Update the progress of an existing dialog |
| 703 | * @param progress latest progress to be displayed |
| 704 | */ |
| 705 | public void updateProgress(int progress) { |
| 706 | mProgressString = getProgressString(progress); |
| 707 | AlertDialog dialog = (AlertDialog) getDialog(); |
| 708 | dialog.setMessage(mProgressString); |
| 709 | } |
| 710 | |
| 711 | @Override |
| 712 | public Dialog onCreateDialog(Bundle savedInstanceState) { |
| 713 | Context context = getActivity(); |
| 714 | if (savedInstanceState != null) { |
| 715 | mProgressString = savedInstanceState.getString(EXTRA_PROGRESS_STRING); |
| 716 | } |
| 717 | if (mProgressString == null) { |
| 718 | mProgressString = getProgressString(getTargetRequestCode()); |
| 719 | } |
| 720 | final AccountCheckSettingsFragment target = |
| 721 | (AccountCheckSettingsFragment) getTargetFragment(); |
| 722 | |
| 723 | ProgressDialog dialog = new ProgressDialog(context); |
| 724 | dialog.setIndeterminate(true); |
| 725 | dialog.setMessage(mProgressString); |
| 726 | dialog.setButton(DialogInterface.BUTTON_NEGATIVE, |
| 727 | context.getString(R.string.cancel_action), |
| 728 | new DialogInterface.OnClickListener() { |
| 729 | public void onClick(DialogInterface dialog, int which) { |
| 730 | dismiss(); |
| 731 | target.onCheckingDialogCancel(); |
| 732 | } |
| 733 | }); |
| 734 | return dialog; |
| 735 | } |
| 736 | |
Andy Stadler | 992b7e7 | 2010-11-05 15:54:28 -0700 | [diff] [blame] | 737 | /** |
| 738 | * Listen for cancellation, which can happen from places other than the |
| 739 | * negative button (e.g. touching outside the dialog), and stop the checker |
| 740 | */ |
| 741 | @Override |
| 742 | public void onCancel(DialogInterface dialog) { |
| 743 | AccountCheckSettingsFragment target = |
| 744 | (AccountCheckSettingsFragment) getTargetFragment(); |
| 745 | target.onCheckingDialogCancel(); |
| 746 | super.onCancel(dialog); |
| 747 | } |
| 748 | |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 749 | @Override |
| 750 | public void onSaveInstanceState(Bundle outState) { |
| 751 | super.onSaveInstanceState(outState); |
| 752 | outState.putString(EXTRA_PROGRESS_STRING, mProgressString); |
| 753 | } |
| 754 | |
| 755 | /** |
| 756 | * Convert progress to message |
| 757 | */ |
| 758 | private String getProgressString(int progress) { |
| 759 | int stringId = 0; |
| 760 | switch (progress) { |
| 761 | case STATE_CHECK_AUTODISCOVER: |
| 762 | stringId = R.string.account_setup_check_settings_retr_info_msg; |
| 763 | break; |
| 764 | case STATE_CHECK_INCOMING: |
| 765 | stringId = R.string.account_setup_check_settings_check_incoming_msg; |
| 766 | break; |
| 767 | case STATE_CHECK_OUTGOING: |
| 768 | stringId = R.string.account_setup_check_settings_check_outgoing_msg; |
| 769 | break; |
| 770 | } |
| 771 | return getActivity().getString(stringId); |
| 772 | } |
| 773 | } |
| 774 | |
| 775 | /** |
| 776 | * The standard error dialog. Calls back to onErrorDialogButton(). |
| 777 | */ |
| 778 | public static class ErrorDialog extends DialogFragment { |
Todd Kennedy | 040ddf6 | 2011-02-04 09:29:13 -0800 | [diff] [blame] | 779 | @SuppressWarnings("hiding") |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 780 | public final static String TAG = "ErrorDialog"; |
| 781 | |
| 782 | // Bundle keys for arguments |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 783 | private final static String ARGS_MESSAGE = "ErrorDialog.Message"; |
| 784 | private final static String ARGS_EXCEPTION_ID = "ErrorDialog.ExceptionId"; |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 785 | |
Mindy Pereira | 92922ea | 2011-09-26 11:05:04 -0700 | [diff] [blame] | 786 | /** |
| 787 | * Use {@link #newInstance} This public constructor is still required so |
| 788 | * that DialogFragment state can be automatically restored by the |
| 789 | * framework. |
| 790 | */ |
| 791 | public ErrorDialog() { |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 792 | } |
| 793 | |
| 794 | public static ErrorDialog newInstance(Context context, AccountCheckSettingsFragment target, |
| 795 | MessagingException ex) { |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 796 | ErrorDialog fragment = new ErrorDialog(); |
| 797 | Bundle arguments = new Bundle(); |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 798 | arguments.putString(ARGS_MESSAGE, getErrorString(context, ex)); |
| 799 | arguments.putInt(ARGS_EXCEPTION_ID, ex.getExceptionType()); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 800 | fragment.setArguments(arguments); |
| 801 | fragment.setTargetFragment(target, 0); |
| 802 | return fragment; |
| 803 | } |
| 804 | |
| 805 | @Override |
| 806 | public Dialog onCreateDialog(Bundle savedInstanceState) { |
| 807 | final Context context = getActivity(); |
| 808 | final Bundle arguments = getArguments(); |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 809 | final String message = arguments.getString(ARGS_MESSAGE); |
| 810 | final int exceptionId = arguments.getInt(ARGS_EXCEPTION_ID); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 811 | final AccountCheckSettingsFragment target = |
| 812 | (AccountCheckSettingsFragment) getTargetFragment(); |
| 813 | |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 814 | AlertDialog.Builder builder = new AlertDialog.Builder(context) |
Makoto Onuki | 49518bb | 2011-01-18 10:14:14 -0800 | [diff] [blame] | 815 | .setIconAttribute(android.R.attr.alertDialogIcon) |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 816 | .setTitle(context.getString(R.string.account_setup_failed_dlg_title)) |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 817 | .setMessage(message) |
| 818 | .setCancelable(true); |
| 819 | |
| 820 | if (exceptionId == MessagingException.CLIENT_CERTIFICATE_REQUIRED) { |
| 821 | // Certificate error - show two buttons so the host fragment can auto pop |
| 822 | // into the appropriate flow. |
| 823 | builder.setPositiveButton( |
| 824 | context.getString(android.R.string.ok), |
| 825 | new DialogInterface.OnClickListener() { |
| 826 | public void onClick(DialogInterface dialog, int which) { |
| 827 | dismiss(); |
| 828 | target.onEditCertificateOk(); |
| 829 | } |
| 830 | }); |
| 831 | builder.setNegativeButton( |
| 832 | context.getString(android.R.string.cancel), |
| 833 | new DialogInterface.OnClickListener() { |
| 834 | public void onClick(DialogInterface dialog, int which) { |
| 835 | dismiss(); |
| 836 | target.onErrorDialogEditButton(); |
| 837 | } |
| 838 | }); |
| 839 | |
| 840 | } else { |
| 841 | // "Normal" error - just use a single "Edit details" button. |
| 842 | builder.setPositiveButton( |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 843 | context.getString(R.string.account_setup_failed_dlg_edit_details_action), |
| 844 | new DialogInterface.OnClickListener() { |
| 845 | public void onClick(DialogInterface dialog, int which) { |
| 846 | dismiss(); |
| 847 | target.onErrorDialogEditButton(); |
| 848 | } |
Ben Komalo | 715ee4e | 2011-09-13 11:34:22 -0700 | [diff] [blame] | 849 | }); |
| 850 | } |
| 851 | |
| 852 | return builder.create(); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 853 | } |
| 854 | |
| 855 | } |
| 856 | |
| 857 | /** |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 858 | * The "security required" error dialog. This is presented whenever an exchange account |
| 859 | * reports that it will require security policy control, and provide the user with the |
| 860 | * opportunity to accept or deny this. |
| 861 | * |
| 862 | * If the user clicks OK, calls onSecurityRequiredDialogResultOk(true) which reports back |
| 863 | * to the target as if the settings check was "ok". If the user clicks "cancel", calls |
| 864 | * onSecurityRequiredDialogResultOk(false) which simply closes the checker (this is the |
| 865 | * same as any other failed check.) |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 866 | */ |
| 867 | public static class SecurityRequiredDialog extends DialogFragment { |
Todd Kennedy | 040ddf6 | 2011-02-04 09:29:13 -0800 | [diff] [blame] | 868 | @SuppressWarnings("hiding") |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 869 | public final static String TAG = "SecurityRequiredDialog"; |
| 870 | |
| 871 | // Bundle keys for arguments |
| 872 | private final static String ARGS_HOST_NAME = "SecurityRequiredDialog.HostName"; |
| 873 | |
| 874 | public static SecurityRequiredDialog newInstance(AccountCheckSettingsFragment target, |
| 875 | String hostName) { |
| 876 | SecurityRequiredDialog fragment = new SecurityRequiredDialog(); |
| 877 | Bundle arguments = new Bundle(); |
| 878 | arguments.putString(ARGS_HOST_NAME, hostName); |
| 879 | fragment.setArguments(arguments); |
| 880 | fragment.setTargetFragment(target, 0); |
| 881 | return fragment; |
| 882 | } |
| 883 | |
| 884 | @Override |
| 885 | public Dialog onCreateDialog(Bundle savedInstanceState) { |
| 886 | final Context context = getActivity(); |
| 887 | final Bundle arguments = getArguments(); |
| 888 | final String hostName = arguments.getString(ARGS_HOST_NAME); |
| 889 | final AccountCheckSettingsFragment target = |
| 890 | (AccountCheckSettingsFragment) getTargetFragment(); |
| 891 | |
| 892 | return new AlertDialog.Builder(context) |
Makoto Onuki | 49518bb | 2011-01-18 10:14:14 -0800 | [diff] [blame] | 893 | .setIconAttribute(android.R.attr.alertDialogIcon) |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 894 | .setTitle(context.getString(R.string.account_setup_security_required_title)) |
| 895 | .setMessage(context.getString( |
| 896 | R.string.account_setup_security_policies_required_fmt, hostName)) |
| 897 | .setCancelable(true) |
| 898 | .setPositiveButton( |
| 899 | context.getString(R.string.okay_action), |
| 900 | new DialogInterface.OnClickListener() { |
| 901 | public void onClick(DialogInterface dialog, int which) { |
| 902 | dismiss(); |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 903 | target.onSecurityRequiredDialogResultOk(true); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 904 | } |
| 905 | }) |
| 906 | .setNegativeButton( |
| 907 | context.getString(R.string.cancel_action), |
| 908 | new DialogInterface.OnClickListener() { |
| 909 | public void onClick(DialogInterface dialog, int which) { |
| 910 | dismiss(); |
Andrew Stadler | 2731aef | 2010-09-13 14:04:45 -0700 | [diff] [blame] | 911 | target.onSecurityRequiredDialogResultOk(false); |
Andrew Stadler | 55110ca | 2010-09-07 22:20:51 -0700 | [diff] [blame] | 912 | } |
| 913 | }) |
| 914 | .create(); |
| 915 | } |
| 916 | |
| 917 | } |
| 918 | |
| 919 | } |