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 com.android.email.AccountBackupRestore; 20 import com.android.email.R; 21 import com.android.email.Utility; 22 import com.android.email.activity.Welcome; 23 import com.android.email.provider.EmailContent; 24 import com.android.email.provider.EmailContent.Account; 25 import com.android.email.provider.EmailContent.AccountColumns; 26 import com.android.email.provider.EmailContent.HostAuth; 27 28 import android.app.Activity; 29 import android.content.ContentUris; 30 import android.content.ContentValues; 31 import android.content.Intent; 32 import android.database.Cursor; 33 import android.os.AsyncTask; 34 import android.os.Bundle; 35 import android.text.Editable; 36 import android.text.TextWatcher; 37 import android.text.method.TextKeyListener; 38 import android.text.method.TextKeyListener.Capitalize; 39 import android.view.View; 40 import android.view.View.OnClickListener; 41 import android.widget.Button; 42 import android.widget.EditText; 43 44 public class AccountSetupNames extends Activity implements OnClickListener { 45 private static final String EXTRA_ACCOUNT_ID = "accountId"; 46 private static final String EXTRA_EAS_FLOW = "easFlow"; 47 private static final int REQUEST_SECURITY = 0; 48 49 private EditText mDescription; 50 private EditText mName; 51 private Account mAccount; 52 private Button mDoneButton; 53 private boolean mEasAccount = false; 54 55 private CheckAccountStateTask mCheckAccountStateTask; 56 57 private static final int ACCOUNT_INFO_COLUMN_FLAGS = 0; 58 private static final int ACCOUNT_INFO_COLUMN_SECURITY_FLAGS = 1; 59 private static final String[] ACCOUNT_INFO_PROJECTION = new String[] { 60 AccountColumns.FLAGS, AccountColumns.SECURITY_FLAGS }; 61 62 public static void actionSetNames(Activity fromActivity, long accountId, boolean easFlowMode) { 63 Intent i = new Intent(fromActivity, AccountSetupNames.class); 64 i.putExtra(EXTRA_ACCOUNT_ID, accountId); 65 i.putExtra(EXTRA_EAS_FLOW, easFlowMode); 66 fromActivity.startActivity(i); 67 } 68 69 @Override 70 public void onCreate(Bundle savedInstanceState) { 71 super.onCreate(savedInstanceState); 72 setContentView(R.layout.account_setup_names); 73 mDescription = (EditText)findViewById(R.id.account_description); 74 mName = (EditText)findViewById(R.id.account_name); 75 mDoneButton = (Button)findViewById(R.id.done); 76 mDoneButton.setOnClickListener(this); 77 78 TextWatcher validationTextWatcher = new TextWatcher() { 79 public void afterTextChanged(Editable s) { 80 validateFields(); 81 } 82 83 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 84 } 85 86 public void onTextChanged(CharSequence s, int start, int before, int count) { 87 } 88 }; 89 mName.addTextChangedListener(validationTextWatcher); 90 91 mName.setKeyListener(TextKeyListener.getInstance(false, Capitalize.WORDS)); 92 93 long accountId = getIntent().getLongExtra(EXTRA_ACCOUNT_ID, -1); 94 mAccount = EmailContent.Account.restoreAccountWithId(this, accountId); 95 // Shouldn't happen, but it could 96 if (mAccount == null) { 97 onBackPressed(); 98 return; 99 } 100 // Get the hostAuth for receiving 101 HostAuth hostAuth = HostAuth.restoreHostAuthWithId(this, mAccount.mHostAuthKeyRecv); 102 if (hostAuth == null) { 103 onBackPressed(); 104 } 105 106 // Remember whether we're an EAS account, since it doesn't require the user name field 107 mEasAccount = hostAuth.mProtocol.equals("eas"); 108 if (mEasAccount) { 109 mName.setVisibility(View.GONE); 110 findViewById(R.id.account_name_label).setVisibility(View.GONE); 111 } 112 /* 113 * Since this field is considered optional, we don't set this here. If 114 * the user fills in a value we'll reset the current value, otherwise we 115 * just leave the saved value alone. 116 */ 117 // mDescription.setText(mAccount.getDescription()); 118 if (mAccount != null && mAccount.getSenderName() != null) { 119 mName.setText(mAccount.getSenderName()); 120 } 121 122 // Make sure the "done" button is in the proper state 123 validateFields(); 124 } 125 126 @Override 127 protected void onDestroy() { 128 super.onDestroy(); 129 130 if (mCheckAccountStateTask != null && 131 mCheckAccountStateTask.getStatus() != CheckAccountStateTask.Status.FINISHED) { 132 mCheckAccountStateTask.cancel(true); 133 mCheckAccountStateTask = null; 134 } 135 } 136 137 /** 138 * TODO: Validator should also trim the name string before checking it. 139 */ 140 private void validateFields() { 141 if (!mEasAccount) { 142 mDoneButton.setEnabled(Utility.requiredFieldValid(mName)); 143 } 144 Utility.setCompoundDrawablesAlpha(mDoneButton, mDoneButton.isEnabled() ? 255 : 128); 145 } 146 147 @Override 148 public void onBackPressed() { 149 boolean easFlowMode = getIntent().getBooleanExtra(EXTRA_EAS_FLOW, false); 150 if (easFlowMode) { 151 AccountSetupBasics.actionAccountCreateFinishedEas(this); 152 } else { 153 if (mAccount != null) { 154 AccountSetupBasics.actionAccountCreateFinished(this, mAccount.mId); 155 } else { 156 // Safety check here; If mAccount is null (due to external issues or bugs) 157 // just rewind back to Welcome, which can handle any configuration of accounts 158 Welcome.actionStart(this); 159 } 160 } 161 finish(); 162 } 163 164 /** 165 * After having a chance to input the display names, we normally jump directly to the 166 * inbox for the new account. However if we're in EAS flow mode (externally-launched 167 * account creation) we simply "pop" here which should return us to the Accounts activities. 168 * 169 * TODO: Validator should also trim the description string before checking it. 170 */ 171 private void onNext() { 172 if (Utility.requiredFieldValid(mDescription)) { 173 mAccount.setDisplayName(mDescription.getText().toString()); 174 } 175 String name = mName.getText().toString(); 176 mAccount.setSenderName(name); 177 ContentValues cv = new ContentValues(); 178 cv.put(AccountColumns.DISPLAY_NAME, mAccount.getDisplayName()); 179 cv.put(AccountColumns.SENDER_NAME, name); 180 mAccount.update(this, cv); 181 // Update the backup (side copy) of the accounts 182 AccountBackupRestore.backupAccounts(this); 183 184 // Before proceeding, launch an AsyncTask to test the account for any syncing problems, 185 // and if there's a problem, bring up the UI to update the security level. 186 mCheckAccountStateTask = new CheckAccountStateTask(mAccount.mId); 187 mCheckAccountStateTask.execute(); 188 } 189 190 public void onClick(View v) { 191 switch (v.getId()) { 192 case R.id.done: 193 onNext(); 194 break; 195 } 196 } 197 198 /** 199 * This async task is launched just before exiting. It's a last chance test, before leaving 200 * this activity, for the account being in a "hold" state, and gives the user a chance to 201 * update security, enter a device PIN, etc. for a more seamless account setup experience. 202 * 203 * TODO: If there was *any* indication that security might be required, we could at least 204 * force the DeviceAdmin activation step, without waiting for the initial sync/handshake 205 * to fail. 206 * TODO: If the user doesn't update the security, don't go to the MessageList. 207 */ 208 private class CheckAccountStateTask extends AsyncTask<Void, Void, Boolean> { 209 210 private long mAccountId; 211 212 public CheckAccountStateTask(long accountId) { 213 mAccountId = accountId; 214 } 215 216 @Override 217 protected Boolean doInBackground(Void... params) { 218 Cursor c = AccountSetupNames.this.getContentResolver().query( 219 ContentUris.withAppendedId(Account.CONTENT_URI, mAccountId), 220 ACCOUNT_INFO_PROJECTION, null, null, null); 221 try { 222 if (c.moveToFirst()) { 223 int flags = c.getInt(ACCOUNT_INFO_COLUMN_FLAGS); 224 int securityFlags = c.getInt(ACCOUNT_INFO_COLUMN_SECURITY_FLAGS); 225 if ((flags & Account.FLAGS_SECURITY_HOLD) != 0) { 226 return Boolean.TRUE; 227 } 228 } 229 } finally { 230 c.close(); 231 } 232 233 return Boolean.FALSE; 234 } 235 236 @Override 237 protected void onPostExecute(Boolean isSecurityHold) { 238 if (!isCancelled()) { 239 if (isSecurityHold) { 240 Intent i = AccountSecurity.actionUpdateSecurityIntent( 241 AccountSetupNames.this, mAccountId); 242 AccountSetupNames.this.startActivityForResult(i, REQUEST_SECURITY); 243 } else { 244 onBackPressed(); 245 } 246 } 247 } 248 } 249 250 /** 251 * Handle the eventual result from the security update activity 252 * 253 * TODO: If the user doesn't update the security, don't go to the MessageList. 254 */ 255 @Override 256 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 257 switch (requestCode) { 258 case REQUEST_SECURITY: 259 onBackPressed(); 260 } 261 super.onActivityResult(requestCode, resultCode, data); 262 } 263 264 } 265