Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2012 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 package com.android.mail.ui;
     17 
     18 import com.android.mail.R;
     19 import com.android.mail.analytics.Analytics;
     20 import com.android.mail.providers.Account;
     21 import com.android.mail.providers.MailAppProvider;
     22 import com.android.mail.providers.UIProvider;
     23 import com.android.mail.utils.LogTag;
     24 
     25 import java.util.ArrayList;
     26 
     27 import android.app.ActionBar;
     28 import android.app.Fragment;
     29 import android.app.FragmentTransaction;
     30 import android.app.ListActivity;
     31 import android.app.LoaderManager;
     32 import android.appwidget.AppWidgetManager;
     33 import android.content.ContentResolver;
     34 import android.content.CursorLoader;
     35 import android.content.Intent;
     36 import android.content.Loader;
     37 import android.database.Cursor;
     38 import android.os.AsyncTask;
     39 import android.os.Bundle;
     40 import android.os.Handler;
     41 import android.view.View;
     42 import android.view.View.OnClickListener;
     43 import android.view.ViewGroup;
     44 import android.widget.Button;
     45 import android.widget.ListView;
     46 import android.widget.SimpleCursorAdapter;
     47 import android.widget.TextView;
     48 
     49 /**
     50  * An activity that shows the list of all the available accounts and return the
     51  * one selected in onResult().
     52  */
     53 public class MailboxSelectionActivity extends ListActivity implements OnClickListener,
     54         LoaderManager.LoaderCallbacks<Cursor> {
     55 
     56     // Used to save our instance state
     57     private static final String CREATE_SHORTCUT_KEY = "createShortcut";
     58     private static final String CREATE_WIDGET_KEY = "createWidget";
     59     private static final String WIDGET_ID_KEY = "widgetId";
     60     private static final String WAITING_FOR_ADD_ACCOUNT_RESULT_KEY = "waitingForAddAccountResult";
     61 
     62     private static final String ACCOUNT = "name";
     63     private static final String[] COLUMN_NAMES = { ACCOUNT };
     64     protected static final String LOG_TAG = LogTag.getLogTag();
     65     private static final int RESULT_CREATE_ACCOUNT = 2;
     66     private static final int LOADER_ACCOUNT_CURSOR = 0;
     67     private static final String TAG_WAIT = "wait-fragment";
     68     private final int[] VIEW_IDS = { R.id.mailbox_name };
     69     private boolean mCreateShortcut = false;
     70     private boolean mConfigureWidget = false;
     71     private SimpleCursorAdapter mAdapter;
     72     private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
     73 
     74     // Boolean to indicate that we are waiting for the result from an add account
     75     // operation.  This boolean is necessary, as there is no guarantee on whether the
     76     // AccountManager callback or onResume will be called first.
     77     boolean mWaitingForAddAccountResult = false;
     78 
     79     // Can only do certain actions if the Activity is resumed (e.g. setVisible)
     80     private boolean mResumed = false;
     81     private Handler mHandler = new Handler();
     82     private View mContent;
     83     private View mWait;
     84 
     85     @Override
     86     public void onCreate(Bundle icicle) {
     87         super.onCreate(icicle);
     88         setContentView(R.layout.mailbox_selection_activity);
     89         mContent = findViewById(R.id.content);
     90         mWait = findViewById(R.id.wait);
     91         if (icicle != null) {
     92             restoreState(icicle);
     93         } else {
     94             if (Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
     95                 mCreateShortcut = true;
     96             }
     97             mAppWidgetId = getIntent().getIntExtra(
     98                     AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
     99             if (mAppWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
    100                 mConfigureWidget = true;
    101             }
    102         }
    103         // We set the default title to "Gmail" or "Google Mail" for consistency
    104         // in Task Switcher. If this is for create shortcut or configure widget,
    105         // we should set the title to "Select account".
    106         if (mCreateShortcut || mConfigureWidget) {
    107             setTitle(getResources().getString(R.string.activity_mailbox_selection));
    108             ActionBar actionBar = getActionBar();
    109             if (actionBar != null) {
    110                 actionBar.setIcon(R.mipmap.ic_launcher_shortcut_folder);
    111             }
    112         }
    113         ((Button) findViewById(R.id.first_button)).setOnClickListener(this);
    114 
    115         // Initially, assume that the main view is invisible.  It will be made visible,
    116         // if we display the account list
    117         setVisible(false);
    118         setResult(RESULT_CANCELED);
    119     }
    120 
    121     @Override
    122     protected void onSaveInstanceState(Bundle icicle) {
    123         super.onSaveInstanceState(icicle);
    124 
    125         icicle.putBoolean(CREATE_SHORTCUT_KEY, mCreateShortcut);
    126         icicle.putBoolean(CREATE_WIDGET_KEY, mConfigureWidget);
    127         if (mAppWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
    128             icicle.putInt(WIDGET_ID_KEY, mAppWidgetId);
    129         }
    130         icicle.putBoolean(WAITING_FOR_ADD_ACCOUNT_RESULT_KEY, mWaitingForAddAccountResult);
    131     }
    132 
    133     @Override
    134     public void onStart() {
    135         super.onStart();
    136 
    137         Analytics.getInstance().activityStart(this);
    138     }
    139 
    140     @Override
    141     protected void onStop() {
    142         super.onStop();
    143 
    144         Analytics.getInstance().activityStop(this);
    145     }
    146 
    147     @Override
    148     public void onResume() {
    149         super.onResume();
    150         mResumed = true;
    151         // Only fetch the accounts, if we are not handling a response from the
    152         // launched child activity.
    153         if (!mWaitingForAddAccountResult) {
    154             setupWithAccounts();
    155         }
    156     }
    157 
    158     @Override
    159     public void onPause() {
    160         super.onPause();
    161         mResumed = false;
    162     }
    163 
    164     @Override
    165     public void onNewIntent(Intent intent) {
    166         super.onNewIntent(intent);
    167         setIntent(intent);
    168     }
    169 
    170     /**
    171      * Restores the activity state from a bundle
    172      */
    173     private void restoreState(Bundle icicle) {
    174         if (icicle.containsKey(CREATE_SHORTCUT_KEY)) {
    175             mCreateShortcut = icicle.getBoolean(CREATE_SHORTCUT_KEY);
    176         }
    177         if (icicle.containsKey(CREATE_WIDGET_KEY)) {
    178             mConfigureWidget = icicle.getBoolean(CREATE_WIDGET_KEY);
    179         }
    180         if (icicle.containsKey(WIDGET_ID_KEY)) {
    181             mAppWidgetId = icicle.getInt(WIDGET_ID_KEY);
    182         }
    183         if (icicle.containsKey(WAITING_FOR_ADD_ACCOUNT_RESULT_KEY)) {
    184             mWaitingForAddAccountResult = icicle.getBoolean(WAITING_FOR_ADD_ACCOUNT_RESULT_KEY);
    185         }
    186     }
    187 
    188     private void setupWithAccounts() {
    189         final ContentResolver resolver = getContentResolver();
    190         new AsyncTask<Void, Void, Void>() {
    191             @Override
    192             protected Void doInBackground(Void... params) {
    193                 Cursor cursor = null;
    194                 try {
    195                     cursor = resolver.query(MailAppProvider.getAccountsUri(),
    196                             UIProvider.ACCOUNTS_PROJECTION, null, null, null);
    197                     completeSetupWithAccounts(cursor);
    198                 } finally {
    199                     if (cursor != null) {
    200                         cursor.close();
    201                     }
    202                 }
    203                 return null;
    204             }
    205 
    206         }.execute();
    207     }
    208 
    209     private void completeSetupWithAccounts(final Cursor accounts) {
    210         mHandler.post(new Runnable() {
    211             @Override
    212             public void run() {
    213                 updateAccountList(accounts);
    214             }
    215         });
    216     }
    217 
    218     private void updateAccountList(final Cursor accounts) {
    219         boolean displayAccountList = true;
    220         // Configuring a widget or shortcut.
    221         if (mConfigureWidget || mCreateShortcut) {
    222             if (accounts == null || accounts.getCount() == 0) {
    223                 // No account found, show Add Account screen, for both the widget or
    224                 // shortcut creation process
    225                 // No account found, show Add Account screen, for both the widget or
    226                 // shortcut creation process
    227                 final Intent noAccountIntent = MailAppProvider.getNoAccountIntent(this);
    228                 if (noAccountIntent != null) {
    229                     startActivityForResult(noAccountIntent, RESULT_CREATE_ACCOUNT);
    230                 }
    231                 // No reason to display the account list
    232                 displayAccountList = false;
    233 
    234                 // Indicate that we need to handle the response from the add account action
    235                 // This allows us to process the results that we get in the AddAccountCallback
    236                 mWaitingForAddAccountResult = true;
    237             } else if (mConfigureWidget && accounts.getCount() == 1) {
    238                 mWait.setVisibility(View.GONE);
    239                 // When configuring a widget, if there is only one account, automatically
    240                 // choose that account.
    241                 accounts.moveToFirst();
    242                 selectAccount(new Account(accounts));
    243                 // No reason to display the account list
    244                 displayAccountList = false;
    245             }
    246         }
    247 
    248         if (displayAccountList) {
    249             mContent.setVisibility(View.VISIBLE);
    250             // We are about to display the list, make this activity visible
    251             // But only if the Activity is not paused!
    252             if (mResumed) {
    253                 setVisible(true);
    254             }
    255 
    256             mAdapter = new SimpleCursorAdapter(this, R.layout.mailbox_item, accounts,
    257                     COLUMN_NAMES, VIEW_IDS, 0) {
    258                 @Override
    259                 public View getView(int position, View convertView, ViewGroup parent) {
    260                     View v = super.getView(position, convertView, parent);
    261                     TextView accountView = (TextView) v.findViewById(R.id.mailbox_name);
    262                     accountView.setText(new Account((Cursor) getItem(position)).name);
    263                     return v;
    264                 }
    265             };
    266             setListAdapter(mAdapter);
    267         }
    268     }
    269 
    270     @Override
    271     protected void onListItemClick(ListView l, View v, int position, long id) {
    272         selectAccount(new Account((Cursor)mAdapter.getItem(position)));
    273     }
    274 
    275     private void selectAccount(Account account) {
    276         if (mCreateShortcut || mConfigureWidget) {
    277             // Invoked for a shortcut creation
    278             final Intent intent = new Intent(this, getFolderSelectionActivity());
    279             intent.setFlags(
    280                     Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    281             intent.setAction(mCreateShortcut ?
    282                     Intent.ACTION_CREATE_SHORTCUT : AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
    283             if (mConfigureWidget) {
    284                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
    285             }
    286             intent.putExtra(FolderSelectionActivity.EXTRA_ACCOUNT_SHORTCUT, account);
    287             startActivity(intent);
    288             finish();
    289         } else {
    290             // TODO: (mindyp) handle changing the account for this shortcut.
    291             finish();
    292         }
    293     }
    294 
    295     /**
    296      * Return the class responsible for launching the folder selection activity.
    297      */
    298     protected Class<?> getFolderSelectionActivity() {
    299         return FolderSelectionActivity.class;
    300     }
    301 
    302     @Override
    303     public void onClick(View v) {
    304         final int id = v.getId();
    305         if (id == R.id.first_button) {
    306             setResult(RESULT_CANCELED);
    307             finish();
    308         }
    309     }
    310 
    311     @Override
    312     protected final void onActivityResult(int request, int result, Intent data) {
    313         if (request == RESULT_CREATE_ACCOUNT) {
    314             // We were waiting for the user to create an account
    315             if (result != RESULT_OK) {
    316                 finish();
    317             } else {
    318                 // Watch for accounts to show up!
    319                 // restart the loader to get the updated list of accounts
    320                 getLoaderManager().initLoader(LOADER_ACCOUNT_CURSOR, null, this);
    321                 showWaitFragment(null);
    322             }
    323         }
    324     }
    325 
    326     private void showWaitFragment(Account account) {
    327         WaitFragment fragment = getWaitFragment();
    328         if (fragment != null) {
    329             fragment.updateAccount(account);
    330         } else {
    331             mWait.setVisibility(View.VISIBLE);
    332             replaceFragment(WaitFragment.newInstance(account, true),
    333                     FragmentTransaction.TRANSIT_FRAGMENT_OPEN, TAG_WAIT);
    334         }
    335         mContent.setVisibility(View.GONE);
    336     }
    337 
    338     @Override
    339     public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    340         switch (id) {
    341             case LOADER_ACCOUNT_CURSOR:
    342                 return new CursorLoader(this, MailAppProvider.getAccountsUri(),
    343                         UIProvider.ACCOUNTS_PROJECTION, null, null, null);
    344         }
    345         return null;
    346     }
    347 
    348     private WaitFragment getWaitFragment() {
    349         return (WaitFragment) getFragmentManager().findFragmentByTag(TAG_WAIT);
    350     }
    351 
    352     private int replaceFragment(Fragment fragment, int transition, String tag) {
    353         FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
    354         fragmentTransaction.setTransition(transition);
    355         fragmentTransaction.replace(R.id.wait, fragment, tag);
    356         final int transactionId = fragmentTransaction.commitAllowingStateLoss();
    357         return transactionId;
    358     }
    359 
    360     @Override
    361     public void onLoaderReset(Loader<Cursor> arg0) {
    362         // Do nothing.
    363     }
    364 
    365     @Override
    366     public void onLoadFinished(Loader<Cursor> cursor, Cursor data) {
    367         if (data != null && data.moveToFirst()) {
    368             // there are accounts now!
    369             Account account;
    370             ArrayList<Account> accounts = new ArrayList<Account>();
    371             ArrayList<Account> initializedAccounts = new ArrayList<Account>();
    372             do {
    373                 account = new Account(data);
    374                 if (account.isAccountReady()) {
    375                     initializedAccounts.add(account);
    376                 }
    377                 accounts.add(account);
    378             } while (data.moveToNext());
    379             if (initializedAccounts.size() > 0) {
    380                 mWait.setVisibility(View.GONE);
    381                 getLoaderManager().destroyLoader(LOADER_ACCOUNT_CURSOR);
    382                 mContent.setVisibility(View.VISIBLE);
    383                 updateAccountList(data);
    384             } else {
    385                 // Show "waiting"
    386                 account = accounts.size() > 0 ? accounts.get(0) : null;
    387                 showWaitFragment(account);
    388             }
    389         }
    390     }
    391 
    392     @Override
    393     public void onBackPressed() {
    394         mWaitingForAddAccountResult = false;
    395         // If we are showing the wait fragment, just exit.
    396         if (getWaitFragment() != null) {
    397             finish();
    398         } else {
    399             super.onBackPressed();
    400         }
    401     }
    402 }
    403