Home | History | Annotate | Download | only in setup
      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.accounts.AccountManagerFuture;
     20 import android.accounts.AuthenticatorException;
     21 import android.accounts.OperationCanceledException;
     22 import android.app.Fragment;
     23 import android.app.LoaderManager;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.Loader;
     27 import android.os.Bundle;
     28 import android.os.Handler;
     29 import android.os.RemoteException;
     30 
     31 import com.android.email.provider.EmailProvider;
     32 import com.android.email.service.EmailServiceUtils;
     33 import com.android.email2.ui.MailActivityEmail;
     34 import com.android.emailcommon.provider.Account;
     35 import com.android.emailcommon.service.EmailServiceProxy;
     36 import com.android.mail.preferences.AccountPreferences;
     37 import com.android.mail.ui.MailAsyncTaskLoader;
     38 import com.android.mail.utils.LogUtils;
     39 
     40 import java.io.IOException;
     41 
     42 /**
     43  * This retained headless fragment acts as a container for the multi-step task of creating the
     44  * AccountManager account and saving our account object to the database, as well as some misc
     45  * related background tasks.
     46  */
     47 public class AccountCreationFragment extends Fragment {
     48     public static final String TAG = "AccountCreationFragment";
     49 
     50     public static final int REQUEST_CODE_ACCEPT_POLICIES = 1;
     51 
     52     private static final String ACCOUNT_TAG = "account";
     53     private static final String SYNC_EMAIL_TAG = "email";
     54     private static final String SYNC_CALENDAR_TAG = "calendar";
     55     private static final String SYNC_CONTACTS_TAG = "contacts";
     56     private static final String NOTIFICATIONS_TAG = "notifications";
     57 
     58     private static final String SAVESTATE_STAGE = "AccountCreationFragment.stage";
     59     private static final int STAGE_BEFORE_ACCOUNT_SECURITY = 0;
     60     private static final int STAGE_REFRESHING_ACCOUNT = 1;
     61     private static final int STAGE_WAITING_FOR_ACCOUNT_SECURITY = 2;
     62     private static final int STAGE_AFTER_ACCOUNT_SECURITY = 3;
     63     private int mStage = 0;
     64 
     65     private Context mAppContext;
     66     private final Handler mHandler;
     67 
     68     public interface Callback {
     69         void onAccountCreationFragmentComplete();
     70         void destroyAccountCreationFragment();
     71         void showCreateAccountErrorDialog();
     72         void setAccount(Account account);
     73     }
     74 
     75     public AccountCreationFragment() {
     76         mHandler = new Handler();
     77     }
     78 
     79     public static AccountCreationFragment newInstance(Account account, boolean syncEmail,
     80             boolean syncCalendar, boolean syncContacts, boolean enableNotifications) {
     81         final Bundle args = new Bundle(5);
     82         args.putParcelable(AccountCreationFragment.ACCOUNT_TAG, account);
     83         args.putBoolean(AccountCreationFragment.SYNC_EMAIL_TAG, syncEmail);
     84         args.putBoolean(AccountCreationFragment.SYNC_CALENDAR_TAG, syncCalendar);
     85         args.putBoolean(AccountCreationFragment.SYNC_CONTACTS_TAG, syncContacts);
     86         args.putBoolean(AccountCreationFragment.NOTIFICATIONS_TAG, enableNotifications);
     87 
     88         final AccountCreationFragment f = new AccountCreationFragment();
     89         f.setArguments(args);
     90         return f;
     91     }
     92 
     93     @Override
     94     public void onCreate(Bundle savedInstanceState) {
     95         super.onCreate(savedInstanceState);
     96         setRetainInstance(true);
     97         if (savedInstanceState != null) {
     98             mStage = savedInstanceState.getInt(SAVESTATE_STAGE);
     99         }
    100     }
    101 
    102     @Override
    103     public void onActivityCreated(Bundle savedInstanceState) {
    104         super.onActivityCreated(savedInstanceState);
    105         mAppContext = getActivity().getApplicationContext();
    106     }
    107 
    108     @Override
    109     public void onSaveInstanceState(Bundle outState) {
    110         super.onSaveInstanceState(outState);
    111         outState.putInt(SAVESTATE_STAGE, mStage);
    112     }
    113 
    114     @Override
    115     public void onResume() {
    116         super.onResume();
    117 
    118         switch (mStage) {
    119             case STAGE_BEFORE_ACCOUNT_SECURITY:
    120                 kickBeforeAccountSecurityLoader();
    121                 break;
    122             case STAGE_REFRESHING_ACCOUNT:
    123                 kickRefreshingAccountLoader();
    124                 break;
    125             case STAGE_WAITING_FOR_ACCOUNT_SECURITY:
    126                 // TODO: figure out when we might get here and what to do if we do
    127                 break;
    128             case STAGE_AFTER_ACCOUNT_SECURITY:
    129                 kickAfterAccountSecurityLoader();
    130                 break;
    131         }
    132     }
    133 
    134     private void kickBeforeAccountSecurityLoader() {
    135         final LoaderManager loaderManager = getLoaderManager();
    136 
    137         loaderManager.destroyLoader(STAGE_REFRESHING_ACCOUNT);
    138         loaderManager.destroyLoader(STAGE_AFTER_ACCOUNT_SECURITY);
    139         loaderManager.initLoader(STAGE_BEFORE_ACCOUNT_SECURITY, getArguments(),
    140                 new BeforeAccountSecurityCallbacks());
    141     }
    142 
    143     private void kickRefreshingAccountLoader() {
    144         final LoaderManager loaderManager = getLoaderManager();
    145 
    146         loaderManager.destroyLoader(STAGE_BEFORE_ACCOUNT_SECURITY);
    147         loaderManager.destroyLoader(STAGE_AFTER_ACCOUNT_SECURITY);
    148         loaderManager.initLoader(STAGE_REFRESHING_ACCOUNT, getArguments(),
    149                 new RefreshAccountCallbacks());
    150     }
    151 
    152     private void kickAfterAccountSecurityLoader() {
    153         final LoaderManager loaderManager = getLoaderManager();
    154 
    155         loaderManager.destroyLoader(STAGE_BEFORE_ACCOUNT_SECURITY);
    156         loaderManager.destroyLoader(STAGE_REFRESHING_ACCOUNT);
    157         loaderManager.initLoader(STAGE_AFTER_ACCOUNT_SECURITY, getArguments(),
    158                 new AfterAccountSecurityCallbacks());
    159     }
    160 
    161     private class BeforeAccountSecurityCallbacks
    162             implements LoaderManager.LoaderCallbacks<Boolean> {
    163         public BeforeAccountSecurityCallbacks() {}
    164 
    165         @Override
    166         public Loader<Boolean> onCreateLoader(int id, Bundle args) {
    167             final Account account = args.getParcelable(ACCOUNT_TAG);
    168             final boolean email = args.getBoolean(SYNC_EMAIL_TAG);
    169             final boolean calendar = args.getBoolean(SYNC_CALENDAR_TAG);
    170             final boolean contacts = args.getBoolean(SYNC_CONTACTS_TAG);
    171             final boolean notificationsEnabled = args.getBoolean(NOTIFICATIONS_TAG);
    172 
    173             /**
    174              * Task loader returns true if we created the account, false if we bailed out.
    175              */
    176             return new MailAsyncTaskLoader<Boolean>(mAppContext) {
    177                 @Override
    178                 protected void onDiscardResult(Boolean result) {}
    179 
    180                 @Override
    181                 public Boolean loadInBackground() {
    182                     // Set the incomplete flag here to avoid reconciliation issues
    183                     account.mFlags |= Account.FLAGS_INCOMPLETE;
    184 
    185                     AccountSettingsUtils.commitSettings(mAppContext, account);
    186                     final AccountManagerFuture<Bundle> future =
    187                             EmailServiceUtils.setupAccountManagerAccount(mAppContext, account,
    188                                     email, calendar, contacts, null);
    189 
    190                     boolean createSuccess = false;
    191                     try {
    192                         future.getResult();
    193                         createSuccess = true;
    194                     } catch (OperationCanceledException e) {
    195                         LogUtils.d(LogUtils.TAG, "addAccount was canceled");
    196                     } catch (IOException e) {
    197                         LogUtils.d(LogUtils.TAG, "addAccount failed: " + e);
    198                     } catch (AuthenticatorException e) {
    199                         LogUtils.d(LogUtils.TAG, "addAccount failed: " + e);
    200                     }
    201                     if (!createSuccess) {
    202                         return false;
    203                     }
    204                     // We can move the notification setting to the inbox FolderPreferences
    205                     // later, once we know what the inbox is
    206                     new AccountPreferences(mAppContext, account.getEmailAddress())
    207                             .setDefaultInboxNotificationsEnabled(notificationsEnabled);
    208 
    209                     // Now that AccountManager account creation is complete, clear the
    210                     // INCOMPLETE flag
    211                     account.mFlags &= ~Account.FLAGS_INCOMPLETE;
    212                     AccountSettingsUtils.commitSettings(mAppContext, account);
    213 
    214                     return true;
    215                 }
    216             };
    217         }
    218 
    219         @Override
    220         public void onLoadFinished(Loader<Boolean> loader, Boolean success) {
    221             if (success == null || !isResumed()) {
    222                 return;
    223             }
    224             if (success) {
    225                 mStage = STAGE_REFRESHING_ACCOUNT;
    226                 kickRefreshingAccountLoader();
    227             } else {
    228                 final Callback callback = (Callback) getActivity();
    229                 mHandler.post(new Runnable() {
    230                     @Override
    231                     public void run() {
    232                         if (!isResumed()) {
    233                             return;
    234                         }
    235                         // Can't do this from within onLoadFinished
    236                         callback.destroyAccountCreationFragment();
    237                         callback.showCreateAccountErrorDialog();
    238                     }
    239                 });
    240             }
    241         }
    242 
    243         @Override
    244         public void onLoaderReset(Loader<Boolean> loader) {}
    245     }
    246 
    247     private class RefreshAccountCallbacks implements LoaderManager.LoaderCallbacks<Account> {
    248 
    249         @Override
    250         public Loader<Account> onCreateLoader(int id, Bundle args) {
    251             final Account account = args.getParcelable(ACCOUNT_TAG);
    252             return new MailAsyncTaskLoader<Account>(mAppContext) {
    253                 @Override
    254                 protected void onDiscardResult(Account result) {}
    255 
    256                 @Override
    257                 public Account loadInBackground() {
    258                     account.refresh(mAppContext);
    259                     return account;
    260                 }
    261             };
    262         }
    263 
    264         @Override
    265         public void onLoadFinished(Loader<Account> loader, Account account) {
    266             if (account == null || !isResumed()) {
    267                 return;
    268             }
    269 
    270             getArguments().putParcelable(ACCOUNT_TAG, account);
    271 
    272             if ((account.mFlags & Account.FLAGS_SECURITY_HOLD) != 0) {
    273                 final Intent intent = AccountSecurity
    274                         .actionUpdateSecurityIntent(getActivity(), account.mId, false);
    275                 startActivityForResult(intent, REQUEST_CODE_ACCEPT_POLICIES);
    276                 mStage = STAGE_WAITING_FOR_ACCOUNT_SECURITY;
    277             } else {
    278                 mStage = STAGE_AFTER_ACCOUNT_SECURITY;
    279                 kickAfterAccountSecurityLoader();
    280             }
    281         }
    282 
    283         @Override
    284         public void onLoaderReset(Loader<Account> loader) {}
    285     }
    286 
    287     private class AfterAccountSecurityCallbacks
    288             implements LoaderManager.LoaderCallbacks<Account> {
    289         @Override
    290         public Loader<Account> onCreateLoader(int id, Bundle args) {
    291             final Account account = args.getParcelable(ACCOUNT_TAG);
    292             return new MailAsyncTaskLoader<Account>(mAppContext) {
    293                 @Override
    294                 protected void onDiscardResult(Account result) {}
    295 
    296                 @Override
    297                 public Account loadInBackground() {
    298                     // Clear the security hold flag now
    299                     account.mFlags &= ~Account.FLAGS_SECURITY_HOLD;
    300                     AccountSettingsUtils.commitSettings(mAppContext, account);
    301                     // Start up services based on new account(s)
    302                     EmailProvider.setServicesEnabledSync(mAppContext);
    303                     EmailServiceUtils
    304                             .startService(mAppContext, account.mHostAuthRecv.mProtocol);
    305                     return account;
    306                 }
    307             };
    308         }
    309 
    310         @Override
    311         public void onLoadFinished(final Loader<Account> loader, final Account account) {
    312             // Need to do this from a runnable because this triggers fragment transactions
    313             mHandler.post(new Runnable() {
    314                 @Override
    315                 public void run() {
    316                     if (account == null || !isResumed()) {
    317                         return;
    318                     }
    319 
    320                     // Move to final setup screen
    321                     Callback callback = (Callback) getActivity();
    322                     callback.setAccount(account);
    323                     callback.onAccountCreationFragmentComplete();
    324 
    325                     // Update the folder list (to get our starting folders, e.g. Inbox)
    326                     final EmailServiceProxy proxy = EmailServiceUtils
    327                             .getServiceForAccount(mAppContext, account.mId);
    328                     try {
    329                         proxy.updateFolderList(account.mId);
    330                     } catch (RemoteException e) {
    331                         // It's all good
    332                     }
    333 
    334                 }
    335             });
    336         }
    337 
    338         @Override
    339         public void onLoaderReset(Loader<Account> loader) {}
    340     }
    341 
    342     /**
    343      * This is called after the AccountSecurity activity completes.
    344      */
    345     @Override
    346     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    347         mStage = STAGE_AFTER_ACCOUNT_SECURITY;
    348         // onResume() will be called immediately after this to kick the next loader
    349     }
    350 }
    351