1 /* 2 * Copyright (C) 2014 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.app.AlertDialog; 20 import android.app.Dialog; 21 import android.app.DialogFragment; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.os.Bundle; 25 import android.text.TextUtils; 26 27 import com.android.email.R; 28 import com.android.emailcommon.mail.MessagingException; 29 import com.android.mail.utils.LogUtils; 30 31 public class CheckSettingsErrorDialogFragment extends DialogFragment{ 32 public final static String TAG = "CheckSettingsErrorDialog"; 33 34 public final static int REASON_OTHER = 0; 35 public final static int REASON_AUTHENTICATION_FAILED = 1; 36 public final static int REASON_CERTIFICATE_REQUIRED = 2; 37 38 // Bundle keys for arguments 39 private final static String ARGS_MESSAGE = "CheckSettingsErrorDialog.Message"; 40 private final static String ARGS_REASON = "CheckSettingsErrorDialog.ExceptionId"; 41 42 public interface Callback { 43 /** 44 * Called to indicate the user wants to resolve the error by changing the client certificate 45 */ 46 void onCheckSettingsErrorDialogEditCertificate(); 47 48 /** 49 * Called to indicate the user wants to resolve the error by editing the server settings 50 */ 51 void onCheckSettingsErrorDialogEditSettings(); 52 } 53 54 public CheckSettingsErrorDialogFragment() {} 55 56 /** 57 * @param reason see REASON_* constants 58 * @param message from {@link #getErrorString(Context, MessagingException)} 59 * @return new instance 60 */ 61 public static CheckSettingsErrorDialogFragment newInstance(int reason, String message) { 62 final CheckSettingsErrorDialogFragment fragment = new CheckSettingsErrorDialogFragment(); 63 final Bundle arguments = new Bundle(2); 64 arguments.putString(ARGS_MESSAGE, message); 65 arguments.putInt(ARGS_REASON, reason); 66 fragment.setArguments(arguments); 67 return fragment; 68 } 69 70 @Override 71 public Dialog onCreateDialog(Bundle savedInstanceState) { 72 final Context context = getActivity(); 73 final Bundle arguments = getArguments(); 74 final String message = arguments.getString(ARGS_MESSAGE); 75 final int reason = arguments.getInt(ARGS_REASON); 76 77 setCancelable(true); 78 79 final AlertDialog.Builder builder = new AlertDialog.Builder(context) 80 .setMessage(message); 81 82 // Use a different title when we get 83 // MessagingException.AUTODISCOVER_AUTHENTICATION_FAILED 84 if (reason == REASON_AUTHENTICATION_FAILED) { 85 builder.setTitle(R.string.account_setup_autodiscover_dlg_authfail_title); 86 } else { 87 builder.setIconAttribute(android.R.attr.alertDialogIcon) 88 .setTitle(context.getString(R.string.account_setup_failed_dlg_title)); 89 } 90 91 if (reason == REASON_CERTIFICATE_REQUIRED) { 92 // Certificate error - show two buttons so the host fragment can auto pop 93 // into the appropriate flow. 94 builder.setPositiveButton( 95 context.getString(android.R.string.ok), 96 new DialogInterface.OnClickListener() { 97 @Override 98 public void onClick(DialogInterface dialog, int which) { 99 dismiss(); 100 final Callback callback = (Callback) getActivity(); 101 callback.onCheckSettingsErrorDialogEditCertificate(); 102 } 103 }); 104 builder.setNegativeButton( 105 context.getString(android.R.string.cancel), 106 new DialogInterface.OnClickListener() { 107 @Override 108 public void onClick(DialogInterface dialog, int which) { 109 dialog.cancel(); 110 } 111 }); 112 113 } else { 114 // "Normal" error - just use a single "Edit details" button. 115 builder.setPositiveButton( 116 context.getString(R.string.account_setup_failed_dlg_edit_details_action), 117 new DialogInterface.OnClickListener() { 118 @Override 119 public void onClick(DialogInterface dialog, int which) { 120 dialog.cancel(); 121 } 122 }); 123 } 124 return builder.create(); 125 } 126 127 @Override 128 public void onCancel(DialogInterface dialog) { 129 super.onCancel(dialog); 130 final Callback callback = (Callback) getActivity(); 131 callback.onCheckSettingsErrorDialogEditSettings(); 132 } 133 134 public static int getReasonFromException (MessagingException ex) { 135 final int exceptionCode = ex.getExceptionType(); 136 switch (exceptionCode) { 137 case MessagingException.AUTODISCOVER_AUTHENTICATION_FAILED: 138 case MessagingException.AUTHENTICATION_FAILED: 139 return REASON_AUTHENTICATION_FAILED; 140 case MessagingException.CLIENT_CERTIFICATE_REQUIRED: 141 return REASON_CERTIFICATE_REQUIRED; 142 } 143 return REASON_OTHER; 144 } 145 146 public static String getErrorString(Context context, MessagingException ex) { 147 final int id; 148 String message = ex.getMessage(); 149 if (message != null) { 150 message = message.trim(); 151 } 152 switch (ex.getExceptionType()) { 153 // The remaining exception types are handled by setting the state to 154 // STATE_CHECK_ERROR (above, default) and conversion to specific error strings. 155 case MessagingException.CERTIFICATE_VALIDATION_ERROR: 156 id = TextUtils.isEmpty(message) 157 ? R.string.account_setup_failed_dlg_certificate_message 158 : R.string.account_setup_failed_dlg_certificate_message_fmt; 159 break; 160 case MessagingException.AUTHENTICATION_FAILED: 161 id = R.string.account_setup_failed_dlg_auth_message; 162 break; 163 case MessagingException.AUTODISCOVER_AUTHENTICATION_FAILED: 164 id = R.string.account_setup_autodiscover_dlg_authfail_message; 165 break; 166 case MessagingException.AUTHENTICATION_FAILED_OR_SERVER_ERROR: 167 id = R.string.account_setup_failed_check_credentials_message; 168 break; 169 case MessagingException.IOERROR: 170 id = R.string.account_setup_failed_ioerror; 171 break; 172 case MessagingException.TLS_REQUIRED: 173 id = R.string.account_setup_failed_tls_required; 174 break; 175 case MessagingException.AUTH_REQUIRED: 176 id = R.string.account_setup_failed_auth_required; 177 break; 178 case MessagingException.SECURITY_POLICIES_UNSUPPORTED: 179 id = R.string.account_setup_failed_security_policies_unsupported; 180 // Belt and suspenders here; there should always be a non-empty array here 181 String[] unsupportedPolicies = (String[]) ex.getExceptionData(); 182 if (unsupportedPolicies == null) { 183 LogUtils.w(LogUtils.TAG, "No data for unsupported policies?"); 184 break; 185 } 186 // Build a string, concatenating policies we don't support 187 final StringBuilder sb = new StringBuilder(); 188 boolean first = true; 189 for (String policyName: unsupportedPolicies) { 190 if (first) { 191 first = false; 192 } else { 193 sb.append(", "); 194 } 195 sb.append(policyName); 196 } 197 message = sb.toString(); 198 break; 199 case MessagingException.ACCESS_DENIED: 200 id = R.string.account_setup_failed_access_denied; 201 break; 202 case MessagingException.PROTOCOL_VERSION_UNSUPPORTED: 203 id = R.string.account_setup_failed_protocol_unsupported; 204 break; 205 case MessagingException.GENERAL_SECURITY: 206 id = R.string.account_setup_failed_security; 207 break; 208 case MessagingException.CLIENT_CERTIFICATE_REQUIRED: 209 id = R.string.account_setup_failed_certificate_required; 210 break; 211 case MessagingException.CLIENT_CERTIFICATE_ERROR: 212 id = R.string.account_setup_failed_certificate_inaccessible; 213 break; 214 default: 215 id = TextUtils.isEmpty(message) 216 ? R.string.account_setup_failed_dlg_server_message 217 : R.string.account_setup_failed_dlg_server_message_fmt; 218 break; 219 } 220 return TextUtils.isEmpty(message) 221 ? context.getString(id) 222 : context.getString(id, message); 223 } 224 } 225