Home | History | Annotate | Download | only in setup
      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 android.app.Activity;
     20 import android.content.ContentValues;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.net.Uri;
     24 import android.os.AsyncTask;
     25 import android.os.Bundle;
     26 import android.provider.ContactsContract.Profile;
     27 import android.text.Editable;
     28 import android.text.TextUtils;
     29 import android.text.TextWatcher;
     30 import android.text.method.TextKeyListener;
     31 import android.text.method.TextKeyListener.Capitalize;
     32 import android.view.View;
     33 import android.view.View.OnClickListener;
     34 import android.widget.Button;
     35 import android.widget.EditText;
     36 
     37 import com.android.email.R;
     38 import com.android.email.activity.ActivityHelper;
     39 import com.android.email.activity.UiUtilities;
     40 import com.android.email.activity.Welcome;
     41 import com.android.email.provider.AccountBackupRestore;
     42 import com.android.emailcommon.provider.Account;
     43 import com.android.emailcommon.provider.EmailContent.AccountColumns;
     44 import com.android.emailcommon.provider.HostAuth;
     45 import com.android.emailcommon.utility.EmailAsyncTask;
     46 import com.android.emailcommon.utility.Utility;
     47 
     48 /**
     49  * Final screen of setup process.  Collect account nickname and/or username.
     50  */
     51 public class AccountSetupNames extends AccountSetupActivity implements OnClickListener {
     52     private static final int REQUEST_SECURITY = 0;
     53 
     54     private static final Uri PROFILE_URI = Profile.CONTENT_URI;
     55 
     56     private EditText mDescription;
     57     private EditText mName;
     58     private Button mNextButton;
     59     private boolean mEasAccount = false;
     60 
     61     public static void actionSetNames(Activity fromActivity) {
     62         fromActivity.startActivity(new Intent(fromActivity, AccountSetupNames.class));
     63     }
     64 
     65     @Override
     66     public void onCreate(Bundle savedInstanceState) {
     67         super.onCreate(savedInstanceState);
     68         ActivityHelper.debugSetWindowFlags(this);
     69         setContentView(R.layout.account_setup_names);
     70         mDescription = (EditText) UiUtilities.getView(this, R.id.account_description);
     71         mName = (EditText) UiUtilities.getView(this, R.id.account_name);
     72         View accountNameLabel = UiUtilities.getView(this, R.id.account_name_label);
     73         mNextButton = (Button) UiUtilities.getView(this, R.id.next);
     74         mNextButton.setOnClickListener(this);
     75 
     76         TextWatcher validationTextWatcher = new TextWatcher() {
     77             @Override
     78             public void afterTextChanged(Editable s) {
     79                 validateFields();
     80             }
     81 
     82             @Override
     83             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
     84             }
     85 
     86             @Override
     87             public void onTextChanged(CharSequence s, int start, int before, int count) {
     88             }
     89         };
     90         mName.addTextChangedListener(validationTextWatcher);
     91         mName.setKeyListener(TextKeyListener.getInstance(false, Capitalize.WORDS));
     92 
     93         Account account = SetupData.getAccount();
     94         if (account == null) {
     95             throw new IllegalStateException("unexpected null account");
     96         }
     97         if (account.mHostAuthRecv == null) {
     98             throw new IllegalStateException("unexpected null hostauth");
     99         }
    100         int flowMode = SetupData.getFlowMode();
    101 
    102         if (flowMode != SetupData.FLOW_MODE_FORCE_CREATE
    103                 && flowMode != SetupData.FLOW_MODE_EDIT) {
    104             String accountEmail = account.mEmailAddress;
    105             mDescription.setText(accountEmail);
    106 
    107             // Move cursor to the end so it's easier to erase in case the user doesn't like it.
    108             mDescription.setSelection(accountEmail.length());
    109         }
    110 
    111         // Remember whether we're an EAS account, since it doesn't require the user name field
    112         mEasAccount = HostAuth.SCHEME_EAS.equals(account.mHostAuthRecv.mProtocol);
    113         if (mEasAccount) {
    114             mName.setVisibility(View.GONE);
    115             accountNameLabel.setVisibility(View.GONE);
    116         } else {
    117             if (account != null && account.getSenderName() != null) {
    118                 mName.setText(account.getSenderName());
    119             } else if (flowMode != SetupData.FLOW_MODE_FORCE_CREATE
    120                     && flowMode != SetupData.FLOW_MODE_EDIT) {
    121                 // Attempt to prefill the name field from the profile if we don't have it,
    122                 prefillNameFromProfile();
    123             }
    124         }
    125 
    126         // Make sure the "done" button is in the proper state
    127         validateFields();
    128 
    129         // Proceed immediately if in account creation mode
    130         if (flowMode == SetupData.FLOW_MODE_FORCE_CREATE) {
    131             onNext();
    132         }
    133     }
    134 
    135     private void prefillNameFromProfile() {
    136         new EmailAsyncTask<Void, Void, String>(null) {
    137             @Override
    138             protected String doInBackground(Void... params) {
    139                 String[] projection = new String[] { Profile.DISPLAY_NAME };
    140                 return Utility.getFirstRowString(
    141                         AccountSetupNames.this, PROFILE_URI, projection, null, null, null, 0);
    142             }
    143 
    144             @Override
    145             public void onSuccess(String result) {
    146                 // Views can only be modified on the main thread.
    147                 mName.setText(result);
    148             }
    149         }.executeParallel((Void[]) null);
    150     }
    151 
    152     /**
    153      * Implements OnClickListener
    154      */
    155     @Override
    156     public void onClick(View v) {
    157         switch (v.getId()) {
    158             case R.id.next:
    159                 onNext();
    160                 break;
    161         }
    162     }
    163 
    164     /**
    165      * Check input fields for legal values and enable/disable next button
    166      */
    167     private void validateFields() {
    168         boolean enableNextButton = true;
    169         // Validation is based only on the "user name" field, not shown for EAS accounts
    170         if (!mEasAccount) {
    171             String userName = mName.getText().toString().trim();
    172             if (TextUtils.isEmpty(userName)) {
    173                 enableNextButton = false;
    174                 mName.setError(getString(R.string.account_setup_names_user_name_empty_error));
    175             } else {
    176                 mName.setError(null);
    177             }
    178         }
    179         mNextButton.setEnabled(enableNextButton);
    180     }
    181 
    182     /**
    183      * Block the back key if we are currently processing the "next" key"
    184      */
    185     @Override
    186     public void onBackPressed() {
    187         if (mNextButton.isEnabled()) {
    188             finishActivity();
    189         }
    190     }
    191 
    192     private void finishActivity() {
    193         if (SetupData.getFlowMode() != SetupData.FLOW_MODE_NORMAL) {
    194             AccountSetupBasics.actionAccountCreateFinishedAccountFlow(this);
    195         } else {
    196             Account account = SetupData.getAccount();
    197             if (account != null) {
    198                 AccountSetupBasics.actionAccountCreateFinished(this, account.mId);
    199             } else {
    200                 // Safety check here;  If mAccount is null (due to external issues or bugs)
    201                 // just rewind back to Welcome, which can handle any configuration of accounts
    202                 Welcome.actionStart(this);
    203             }
    204         }
    205         finish();
    206     }
    207 
    208     /**
    209      * After clicking the next button, we'll start an async task to commit the data
    210      * and other steps to finish the creation of the account.
    211      */
    212     private void onNext() {
    213         mNextButton.setEnabled(false); // Protect against double-tap.
    214 
    215         // Update account object from UI
    216         Account account = SetupData.getAccount();
    217         String description = mDescription.getText().toString().trim();
    218         if (!TextUtils.isEmpty(description)) {
    219             account.setDisplayName(description);
    220         }
    221         account.setSenderName(mName.getText().toString().trim());
    222 
    223         // Launch async task for final commit work
    224         // Sicne it's a write task, use the serial executor so even if we ran the task twice
    225         // with different values the result would be consistent.
    226         new FinalSetupTask(account).executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
    227     }
    228 
    229     /**
    230      * Final account setup work is handled in this AsyncTask:
    231      *   Commit final values to provider
    232      *   Trigger account backup
    233      *   Check for security hold
    234      *
    235      * When this completes, we return to UI thread for the following steps:
    236      *   If security hold, dispatch to AccountSecurity activity
    237      *   Otherwise, return to AccountSetupBasics for conclusion.
    238      *
    239      * TODO: If there was *any* indication that security might be required, we could at least
    240      * force the DeviceAdmin activation step, without waiting for the initial sync/handshake
    241      * to fail.
    242      * TODO: If the user doesn't update the security, don't go to the MessageList.
    243      */
    244     private class FinalSetupTask extends AsyncTask<Void, Void, Boolean> {
    245 
    246         private final Account mAccount;
    247         private final Context mContext;
    248 
    249         public FinalSetupTask(Account account) {
    250             mAccount = account;
    251             mContext = AccountSetupNames.this;
    252         }
    253 
    254         @Override
    255         protected Boolean doInBackground(Void... params) {
    256             // Update the account in the database
    257             ContentValues cv = new ContentValues();
    258             cv.put(AccountColumns.DISPLAY_NAME, mAccount.getDisplayName());
    259             cv.put(AccountColumns.SENDER_NAME, mAccount.getSenderName());
    260             mAccount.update(mContext, cv);
    261 
    262             // Update the backup (side copy) of the accounts
    263             AccountBackupRestore.backup(AccountSetupNames.this);
    264 
    265             return Account.isSecurityHold(mContext, mAccount.mId);
    266         }
    267 
    268         @Override
    269         protected void onPostExecute(Boolean isSecurityHold) {
    270             if (!isCancelled()) {
    271                 if (isSecurityHold) {
    272                     Intent i = AccountSecurity.actionUpdateSecurityIntent(
    273                             AccountSetupNames.this, mAccount.mId, false);
    274                     startActivityForResult(i, REQUEST_SECURITY);
    275                 } else {
    276                     finishActivity();
    277                 }
    278             }
    279         }
    280     }
    281 
    282     /**
    283      * Handle the eventual result from the security update activity
    284      *
    285      * TODO: If the user doesn't update the security, don't go to the MessageList.
    286      */
    287     @Override
    288     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    289         switch (requestCode) {
    290             case REQUEST_SECURITY:
    291                 finishActivity();
    292         }
    293         super.onActivityResult(requestCode, resultCode, data);
    294     }
    295 
    296 }
    297