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