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