AbstractActivityController.java revision 1f93668e1186d48b507207841c1ca0529c3de292
1/******************************************************************************* 2 * Copyright (C) 2012 Google Inc. 3 * Licensed to The Android Open Source Project. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 *******************************************************************************/ 17 18package com.android.mail.ui; 19 20import android.app.ActionBar; 21import android.app.ActionBar.LayoutParams; 22import android.app.Activity; 23import android.app.Dialog; 24import android.content.ContentResolver; 25import android.content.Context; 26import android.content.CursorLoader; 27import android.content.Intent; 28import android.content.Loader; 29import android.database.Cursor; 30import android.net.Uri; 31import android.os.AsyncTask; 32import android.os.Bundle; 33import android.os.Handler; 34import android.view.ActionMode; 35import android.view.KeyEvent; 36import android.view.LayoutInflater; 37import android.view.Menu; 38import android.view.MenuInflater; 39import android.view.MenuItem; 40import android.view.MotionEvent; 41import android.view.View; 42import android.widget.LinearLayout; 43import android.widget.Toast; 44 45import com.android.mail.R; 46import com.android.mail.ConversationListContext; 47import com.android.mail.compose.ComposeActivity; 48import com.android.mail.providers.Account; 49import com.android.mail.providers.AccountCacheProvider; 50import com.android.mail.providers.Conversation; 51import com.android.mail.providers.Folder; 52import com.android.mail.providers.Settings; 53import com.android.mail.providers.UIProvider; 54import com.android.mail.providers.UIProvider.AccountCapabilities; 55import com.android.mail.providers.UIProvider.LastSyncResult; 56import com.android.mail.ui.AsyncRefreshTask; 57import com.android.mail.utils.LogUtils; 58import com.android.mail.utils.Utils; 59 60import com.google.common.collect.Sets; 61 62import java.util.Set; 63 64 65/** 66 * This is an abstract implementation of the Activity Controller. This class 67 * knows how to respond to menu items, state changes, layout changes, etc. It 68 * weaves together the views and listeners, dispatching actions to the 69 * respective underlying classes. 70 * <p> 71 * Even though this class is abstract, it should provide default implementations 72 * for most, if not all the methods in the ActivityController interface. This 73 * makes the task of the subclasses easier: OnePaneActivityController and 74 * TwoPaneActivityController can be concise when the common functionality is in 75 * AbstractActivityController. 76 * </p> 77 * <p> 78 * In the Gmail codebase, this was called BaseActivityController 79 * </p> 80 */ 81public abstract class AbstractActivityController implements ActivityController { 82 private static final String SAVED_CONVERSATION = "saved-conversation"; 83 private static final String SAVED_CONVERSATION_POSITION = "saved-conv-pos"; 84 // Keys for serialization of various information in Bundles. 85 private static final String SAVED_LIST_CONTEXT = "saved-list-context"; 86 private static final String SAVED_ACCOUNT = "saved-account"; 87 88 /** 89 * Are we on a tablet device or not. 90 */ 91 public final boolean IS_TABLET_DEVICE; 92 93 protected Account mAccount; 94 protected Folder mFolder; 95 protected ActionBarView mActionBarView; 96 protected final RestrictedActivity mActivity; 97 protected final Context mContext; 98 protected ConversationListContext mConvListContext; 99 private FetchAccountFolderTask mFetchAccountFolderTask; 100 protected Conversation mCurrentConversation; 101 102 protected ConversationListFragment mConversationListFragment; 103 /** 104 * The current mode of the application. All changes in mode are initiated by 105 * the activity controller. View mode changes are propagated to classes that 106 * attach themselves as listeners of view mode changes. 107 */ 108 protected final ViewMode mViewMode; 109 protected ContentResolver mResolver; 110 protected FolderListFragment mFolderListFragment; 111 protected ConversationViewFragment mConversationViewFragment; 112 protected boolean isLoaderInitialized = false; 113 private AsyncRefreshTask mAsyncRefreshTask; 114 115 private MenuItem mRefreshItem; 116 private MenuItem mHelpItem; 117 private View mRefreshActionView; 118 private boolean mRefreshInProgress; 119 private final Handler mHandler = new Handler(); 120 private final Runnable mInvalidateMenu = new Runnable() { 121 @Override 122 public void run() { 123 mActivity.invalidateOptionsMenu(); 124 } 125 }; 126 private final Set<Uri> mCurrentAccountUris = Sets.newHashSet(); 127 protected Settings mCachedSettings; 128 129 protected static final String LOG_TAG = new LogUtils().getLogTag(); 130 private static final int ACCOUNT_CURSOR_LOADER = 0; 131 private static final int ACCOUNT_SETTINGS_LOADER = 1; 132 private static final int FOLDER_CURSOR_LOADER = 2; 133 134 public AbstractActivityController(MailActivity activity, ViewMode viewMode) { 135 mActivity = activity; 136 mViewMode = viewMode; 137 mContext = activity.getApplicationContext(); 138 IS_TABLET_DEVICE = Utils.useTabletUI(mContext); 139 } 140 141 @Override 142 public synchronized void attachConversationList(ConversationListFragment fragment) { 143 // If there is an existing fragment, unregister it 144 if (mConversationListFragment != null) { 145 mViewMode.removeListener(mConversationListFragment); 146 } 147 mConversationListFragment = fragment; 148 // If the current fragment is non-null, add it as a listener. 149 if (fragment != null) { 150 mViewMode.addListener(mConversationListFragment); 151 } 152 } 153 154 @Override 155 public synchronized void attachFolderList(FolderListFragment fragment) { 156 // If there is an existing fragment, unregister it 157 if (mFolderListFragment != null) { 158 mViewMode.removeListener(mFolderListFragment); 159 } 160 mFolderListFragment = fragment; 161 if (fragment != null) { 162 mViewMode.addListener(mFolderListFragment); 163 } 164 } 165 166 @Override 167 public void attachConversationView(ConversationViewFragment conversationViewFragment) { 168 mConversationViewFragment = conversationViewFragment; 169 } 170 171 @Override 172 public void clearSubject() { 173 // TODO(viki): Auto-generated method stub 174 } 175 176 @Override 177 public void enterSearchMode() { 178 // TODO(viki): Auto-generated method stub 179 } 180 181 @Override 182 public void exitSearchMode() { 183 // TODO(viki): Auto-generated method stub 184 } 185 186 @Override 187 public Account getCurrentAccount() { 188 return mAccount; 189 } 190 191 @Override 192 public ConversationListContext getCurrentListContext() { 193 return mConvListContext; 194 } 195 196 @Override 197 public String getHelpContext() { 198 return "Mail"; 199 } 200 201 @Override 202 public int getMode() { 203 return mViewMode.getMode(); 204 } 205 206 @Override 207 public String getUnshownSubject(String subject) { 208 // Calculate how much of the subject is shown, and return the remaining. 209 return null; 210 } 211 212 @Override 213 public void handleConversationLoadError() { 214 // TODO(viki): Auto-generated method stub 215 } 216 217 @Override 218 public void handleSearchRequested() { 219 // TODO(viki): Auto-generated method stub 220 } 221 222 /** 223 * Initialize the action bar. This is not visible to OnePaneController and 224 * TwoPaneController so they cannot override this behavior. 225 */ 226 private void initCustomActionBarView() { 227 ActionBar actionBar = mActivity.getActionBar(); 228 mActionBarView = (MailActionBar) LayoutInflater.from(mContext).inflate( 229 R.layout.actionbar_view, null); 230 231 if (actionBar != null && mActionBarView != null) { 232 // Why have a different variable for the same thing? We should apply 233 // the same actions 234 // on mActionBarView instead. 235 mActionBarView.initialize(mActivity, this, mViewMode, actionBar); 236 actionBar.setCustomView((LinearLayout) mActionBarView, new ActionBar.LayoutParams( 237 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); 238 actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, 239 ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_TITLE); 240 } 241 } 242 243 /** 244 * Returns whether the conversation list fragment is visible or not. 245 * Different layouts will have their own notion on the visibility of 246 * fragments, so this method needs to be overriden. 247 * 248 * @return 249 */ 250 protected abstract boolean isConversationListVisible(); 251 252 @Override 253 public void onAccountChanged(Account account) { 254 if (!account.equals(mAccount)) { 255 mAccount = account; 256 onSettingsChanged(null); 257 restartSettingsLoader(); 258 mActionBarView.setAccount(mAccount); 259 // Account changed; existing folder is invalid. 260 mFolder = null; 261 fetchAccountFolderInfo(); 262 updateHelpMenuItem(); 263 } 264 } 265 266 267 private void restartSettingsLoader() { 268 if (mAccount.settingsQueryUri != null) { 269 mActivity.getLoaderManager().restartLoader(ACCOUNT_SETTINGS_LOADER, null, this); 270 } 271 } 272 273 public void onSettingsChanged(Settings settings) { 274 mCachedSettings = settings; 275 resetActionBarIcon(); 276 } 277 278 @Override 279 public Settings getSettings() { 280 return mCachedSettings; 281 } 282 283 private void fetchAccountFolderInfo() { 284 if (mFetchAccountFolderTask != null) { 285 mFetchAccountFolderTask.cancel(true); 286 } 287 mFetchAccountFolderTask = new FetchAccountFolderTask(); 288 mFetchAccountFolderTask.execute(); 289 } 290 291 @Override 292 public void onFolderChanged(Folder folder) { 293 if (!folder.equals(mFolder)) { 294 setFolder(folder); 295 mConvListContext = ConversationListContext.forFolder(mContext, mAccount, mFolder); 296 showConversationList(mConvListContext); 297 } 298 } 299 300 private void setFolder(Folder folder) { 301 // Start watching folder for sync status. 302 if (!folder.equals(mFolder)) { 303 mRefreshInProgress = false; 304 mFolder = folder; 305 mActionBarView.setFolder(mFolder); 306 mActivity.getLoaderManager().restartLoader(FOLDER_CURSOR_LOADER, null, this); 307 } 308 } 309 310 @Override 311 public void onActionModeFinished(ActionMode mode) { 312 // TODO(viki): Auto-generated method stub 313 } 314 315 @Override 316 public void onActionModeStarted(ActionMode mode) { 317 // TODO(viki): Auto-generated method stub 318 } 319 320 @Override 321 public void onActivityResult(int requestCode, int resultCode, Intent data) { 322 // TODO(viki): Auto-generated method stub 323 } 324 325 @Override 326 public void onConversationListVisibilityChanged(boolean visible) { 327 // TODO(viki): Auto-generated method stub 328 } 329 330 /** 331 * By default, doing nothing is right. A two-pane controller will need to 332 * override this. 333 */ 334 @Override 335 public void onConversationVisibilityChanged(boolean visible) { 336 // Do nothing. 337 return; 338 } 339 340 @Override 341 public boolean onCreate(Bundle savedState) { 342 // Initialize the action bar view. 343 initCustomActionBarView(); 344 // Allow shortcut keys to function for the ActionBar and menus. 345 mActivity.setDefaultKeyMode(Activity.DEFAULT_KEYS_SHORTCUT); 346 mResolver = mActivity.getContentResolver(); 347 348 // All the individual UI components listen for ViewMode changes. This 349 // simplifies the 350 // amount of logic in the AbstractActivityController, but increases the 351 // possibility of 352 // timing-related bugs. 353 mViewMode.addListener(this); 354 assert (mActionBarView != null); 355 mViewMode.addListener(mActionBarView); 356 357 restoreState(savedState); 358 return true; 359 } 360 361 @Override 362 public Dialog onCreateDialog(int id, Bundle bundle) { 363 // TODO(viki): Auto-generated method stub 364 return null; 365 } 366 367 @Override 368 public boolean onCreateOptionsMenu(Menu menu) { 369 MenuInflater inflater = mActivity.getMenuInflater(); 370 inflater.inflate(mActionBarView.getOptionsMenuId(), menu); 371 mRefreshItem = menu.findItem(R.id.refresh); 372 mHelpItem = menu.findItem(R.id.help_info_menu_item); 373 return true; 374 } 375 376 @Override 377 public void onEndBulkOperation() { 378 // TODO(viki): Auto-generated method stub 379 380 } 381 382 @Override 383 public boolean onKeyDown(int keyCode, KeyEvent event) { 384 // TODO(viki): Auto-generated method stub 385 return false; 386 } 387 388 @Override 389 public boolean onOptionsItemSelected(MenuItem item) { 390 int id = item.getItemId(); 391 boolean handled = true; 392 switch (id) { 393 case android.R.id.home: 394 onUpPressed(); 395 break; 396 case R.id.compose: 397 ComposeActivity.compose(mActivity.getActivityContext(), mAccount); 398 break; 399 case R.id.show_all_folders: 400 showFolderList(); 401 break; 402 case R.id.refresh: 403 requestFolderRefresh(); 404 break; 405 case R.id.settings: 406 Utils.showSettings(mActivity.getActivityContext(), mAccount); 407 break; 408 case R.id.help_info_menu_item: 409 // TODO: enable context sensitive help 410 Utils.showHelp(mActivity.getActivityContext(), mAccount.helpIntentUri, null); 411 break; 412 default: 413 handled = false; 414 break; 415 } 416 return handled; 417 } 418 419 private void requestFolderRefresh() { 420 if (mFolder != null) { 421 if (mAsyncRefreshTask != null) { 422 mAsyncRefreshTask.cancel(true); 423 } 424 mAsyncRefreshTask = new AsyncRefreshTask(mContext, mFolder); 425 mAsyncRefreshTask.execute(); 426 } 427 } 428 429 public void onRefreshStarted() { 430 if (!mRefreshInProgress) { 431 mRefreshInProgress = true; 432 mHandler.post(mInvalidateMenu); 433 } 434 } 435 436 public void onRefreshStopped(int status) { 437 if (mRefreshInProgress) { 438 mRefreshInProgress = false; 439 switch (status) { 440 case LastSyncResult.SUCCESS: 441 break; 442 default: 443 Context context = mActivity.getActivityContext(); 444 Toast.makeText(context, Utils.getSyncStatusText(context, status), 445 Toast.LENGTH_LONG).show(); 446 break; 447 } 448 mHandler.post(mInvalidateMenu); 449 } 450 } 451 452 @Override 453 public void onPause() { 454 isLoaderInitialized = false; 455 } 456 457 @Override 458 public void onPrepareDialog(int id, Dialog dialog, Bundle bundle) { 459 // TODO(viki): Auto-generated method stub 460 461 } 462 463 @Override 464 public boolean onPrepareOptionsMenu(Menu menu) { 465 if (mRefreshInProgress) { 466 if (mRefreshItem != null) { 467 if (mRefreshActionView == null) { 468 mRefreshItem.setActionView(R.layout.action_bar_indeterminate_progress); 469 mRefreshActionView = mRefreshItem.getActionView(); 470 } else { 471 mRefreshItem.setActionView(mRefreshActionView); 472 } 473 } 474 } else { 475 if (mRefreshItem != null) { 476 mRefreshItem.setActionView(null); 477 } 478 } 479 480 // Show/hide the help menu item 481 updateHelpMenuItem(); 482 return true; 483 } 484 485 private void updateHelpMenuItem() { 486 if (mHelpItem != null) { 487 mHelpItem.setVisible(mAccount != null 488 && mAccount.supportsCapability(AccountCapabilities.HELP_CONTENT)); 489 } 490 } 491 492 @Override 493 public void onResume() { 494 if (mActionBarView != null) { 495 mActionBarView.onResume(); 496 } 497 } 498 499 @Override 500 public void onSaveInstanceState(Bundle outState) { 501 if (mAccount != null) { 502 LogUtils.d(LOG_TAG, "Saving the account now"); 503 outState.putParcelable(SAVED_ACCOUNT, mAccount); 504 } 505 if (mConvListContext != null) { 506 outState.putBundle(SAVED_LIST_CONTEXT, mConvListContext.toBundle()); 507 } 508 } 509 510 @Override 511 public void onSearchRequested() { 512 // TODO(viki): Auto-generated method stub 513 } 514 515 @Override 516 public void onStartBulkOperation() { 517 // TODO(viki): Auto-generated method stub 518 } 519 520 @Override 521 public void onStartDragMode() { 522 // TODO(viki): Auto-generated method stub 523 } 524 525 @Override 526 public void onStop() { 527 // TODO(viki): Auto-generated method stub 528 } 529 530 @Override 531 public void onStopDragMode() { 532 // TODO(viki): Auto-generated method stub 533 } 534 535 /** 536 * {@inheritDoc} Subclasses must override this to listen to mode changes 537 * from the ViewMode. Subclasses <b>must</b> call the parent's 538 * onViewModeChanged since the parent will handle common state changes. 539 */ 540 @Override 541 public void onViewModeChanged(int newMode) { 542 // Perform any mode specific work here. 543 // reset the action bar icon based on the mode. Why don't the individual 544 // controllers do 545 // this themselves? 546 547 // In conversation list mode, clean up the conversation. 548 if (newMode == ViewMode.CONVERSATION_LIST) { 549 // Clean up the conversation here. 550 } 551 552 // We don't want to invalidate the options menu when switching to 553 // conversation 554 // mode, as it will happen when the conversation finishes loading. 555 if (newMode != ViewMode.CONVERSATION) { 556 mActivity.invalidateOptionsMenu(); 557 } 558 } 559 560 @Override 561 public void onWindowFocusChanged(boolean hasFocus) { 562 // TODO(viki): Auto-generated method stub 563 } 564 565 @Override 566 public void reloadSearch(String string) { 567 // TODO(viki): Auto-generated method stub 568 } 569 570 /** 571 * @param savedState 572 */ 573 protected void restoreListContext(Bundle savedState) { 574 // TODO(viki): Restore the account, the folder, and the conversation, if any. 575 Bundle listContextBundle = savedState.getBundle(SAVED_LIST_CONTEXT); 576 if (listContextBundle != null) { 577 mConvListContext = ConversationListContext.forBundle(listContextBundle); 578 mFolder = mConvListContext.folder; 579 } 580 } 581 582 /** 583 * Restore the state from the previous bundle. Subclasses should call this 584 * method from the parent class, since it performs important UI 585 * initialization. 586 * 587 * @param savedState 588 */ 589 protected void restoreState(Bundle savedState) { 590 if (savedState != null) { 591 restoreListContext(savedState); 592 mAccount = savedState.getParcelable(SAVED_ACCOUNT); 593 restartSettingsLoader(); 594 } else { 595 final Intent intent = mActivity.getIntent(); 596 if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) { 597 if (intent.hasExtra(Utils.EXTRA_ACCOUNT)) { 598 mAccount = ((Account) intent.getParcelableExtra(Utils.EXTRA_ACCOUNT)); 599 mActivity.getLoaderManager().restartLoader(ACCOUNT_SETTINGS_LOADER, null, this); 600 updateHelpMenuItem(); 601 } 602 if (intent.hasExtra(Utils.EXTRA_FOLDER)) { 603 // Open the folder. 604 LogUtils.d(LOG_TAG, "SHOW THE FOLDER at %s", 605 intent.getParcelableExtra(Utils.EXTRA_FOLDER)); 606 onFolderChanged((Folder) intent.getParcelableExtra(Utils.EXTRA_FOLDER)); 607 } 608 if (intent.hasExtra(Utils.EXTRA_CONVERSATION)) { 609 // Open the conversation. 610 LogUtils.d(LOG_TAG, "SHOW THE CONVERSATION at %s", 611 intent.getParcelableExtra(Utils.EXTRA_CONVERSATION)); 612 showConversation((Conversation) intent 613 .getParcelableExtra(Utils.EXTRA_CONVERSATION)); 614 } 615 } 616 } 617 // Create the accounts loader; this loads the acount switch spinner. 618 mActivity.getLoaderManager().initLoader(ACCOUNT_CURSOR_LOADER, null, this); 619 } 620 621 @Override 622 public void setSubject(String subject) { 623 // Do something useful with the subject. This requires changing the 624 // conversation view's subject text. 625 } 626 627 @Override 628 public void startActionBarStatusCursorLoader(String account) { 629 // TODO(viki): Auto-generated method stub 630 } 631 632 @Override 633 public void stopActionBarStatusCursorLoader(String account) { 634 // TODO(viki): Auto-generated method stub 635 } 636 637 @Override 638 public void toggleStar(boolean toggleOn, long conversationId, long maxMessageId) { 639 // TODO(viki): Auto-generated method stub 640 } 641 642 @Override 643 public void onConversationSelected(Conversation conversation) { 644 mCurrentConversation = conversation; 645 showConversation(mCurrentConversation); 646 mViewMode.enterConversationMode(); 647 } 648 649 /** 650 * {@inheritDoc} 651 */ 652 @Override 653 public Loader<Cursor> onCreateLoader(int id, Bundle args) { 654 // Create a loader to listen in on account changes. 655 if (id == ACCOUNT_CURSOR_LOADER) { 656 return new CursorLoader(mContext, AccountCacheProvider.getAccountsUri(), 657 UIProvider.ACCOUNTS_PROJECTION, null, null, null); 658 } else if (id == FOLDER_CURSOR_LOADER) { 659 return new CursorLoader(mActivity.getActivityContext(), mFolder.uri, 660 UIProvider.FOLDERS_PROJECTION, null, null, null); 661 } else if (id == ACCOUNT_SETTINGS_LOADER) { 662 if (mAccount.settingsQueryUri != null) { 663 return new CursorLoader(mActivity.getActivityContext(), mAccount.settingsQueryUri, 664 UIProvider.SETTINGS_PROJECTION, null, null, null); 665 } 666 } 667 return null; 668 } 669 670 private boolean accountsUpdated(Cursor accountCursor) { 671 // Check to see if the current account hasn't been set, or the account cursor is empty 672 if (mAccount == null || !accountCursor.moveToFirst()) { 673 return true; 674 } 675 676 // Check to see if the number of accounts are different, from the number we saw on the last 677 // updated 678 if (mCurrentAccountUris.size() != accountCursor.getCount()) { 679 return true; 680 } 681 682 // Check to see if the account list is different or if the current account is not found in 683 // the cursor. 684 boolean foundCurrentAccount = false; 685 do { 686 final Uri accountUri = 687 Uri.parse(accountCursor.getString(UIProvider.ACCOUNT_URI_COLUMN)); 688 if (!foundCurrentAccount && mAccount.uri.equals(accountUri)) { 689 foundCurrentAccount = true; 690 } 691 692 if (!mCurrentAccountUris.contains(accountUri)) { 693 return true; 694 } 695 } while (accountCursor.moveToNext()); 696 697 // As long as we found the current account, the list hasn't been updated 698 return !foundCurrentAccount; 699 } 700 701 /** 702 * Update the accounts on the device. This currently loads the first account 703 * in the list. 704 * 705 * @param loader 706 * @param accounts cursor into the AccountCache 707 * @return true if the update was successful, false otherwise 708 */ 709 private boolean updateAccounts(Loader<Cursor> loader, Cursor accounts) { 710 if (accounts == null || !accounts.moveToFirst()) { 711 return false; 712 } 713 714 final Account[] allAccounts = Account.getAllAccounts(accounts); 715 716 // Save the uris for the accounts 717 mCurrentAccountUris.clear(); 718 for (Account account : allAccounts) { 719 mCurrentAccountUris.add(account.uri); 720 } 721 722 final Account newAccount; 723 if (mAccount == null || !mCurrentAccountUris.contains(mAccount.uri)) { 724 accounts.moveToFirst(); 725 newAccount = new Account(accounts); 726 } else { 727 newAccount = mAccount; 728 } 729 // only bother updating the account/folder if the new account is different than the 730 // existing one 731 final boolean refetchFolderInfo = !newAccount.equals(mAccount); 732 onAccountChanged(newAccount); 733 734 if(refetchFolderInfo) { 735 fetchAccountFolderInfo(); 736 } 737 738 mActionBarView.setAccounts(allAccounts); 739 return (allAccounts.length > 0); 740 } 741 742 /** 743 * {@inheritDoc} 744 */ 745 @Override 746 public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 747 // We want to reinitialize only if we haven't ever been initialized, or 748 // if the current account has vanished. 749 final int id = loader.getId(); 750 if (id == ACCOUNT_CURSOR_LOADER) { 751 752 final boolean accountListUpdated = accountsUpdated(data); 753 if (!isLoaderInitialized || accountListUpdated) { 754 isLoaderInitialized = updateAccounts(loader, data); 755 } 756 } else if (id == FOLDER_CURSOR_LOADER) { 757 // Check status of the cursor. 758 if (data != null) { 759 data.moveToFirst(); 760 Folder folder = new Folder(data); 761 if (folder.isSyncInProgress()) { 762 onRefreshStarted(); 763 } else { 764 // Stop the spinner here. 765 onRefreshStopped(folder.lastSyncResult); 766 } 767 LogUtils.v(LOG_TAG, "FOLDER STATUS = " + folder.syncStatus); 768 } 769 } else if (id == ACCOUNT_SETTINGS_LOADER) { 770 if (data != null) { 771 data.moveToFirst(); 772 onSettingsChanged(new Settings(data)); 773 } 774 } 775 } 776 777 /** 778 * {@inheritDoc} 779 */ 780 @Override 781 public void onLoaderReset(Loader<Cursor> loader) { 782 // Do nothing for now, since we don't have any state. When a load is 783 // finished, the 784 // onLoadFinished will be called and we will be fine. 785 } 786 787 @Override 788 public void onTouchEvent(MotionEvent event) { 789 if (event.getAction() == MotionEvent.ACTION_DOWN) { 790 int mode = mViewMode.getMode(); 791 if (mode == ViewMode.CONVERSATION_LIST) { 792 mConversationListFragment.onTouchEvent(event); 793 } else if (mode == ViewMode.CONVERSATION) { 794 mConversationViewFragment.onTouchEvent(event); 795 } 796 } 797 } 798 799 private class FetchAccountFolderTask extends AsyncTask<Void, Void, ConversationListContext> { 800 @Override 801 public ConversationListContext doInBackground(Void... params) { 802 return ConversationListContext.forFolder(mContext, mAccount, mFolder); 803 } 804 805 @Override 806 public void onPostExecute(ConversationListContext result) { 807 mConvListContext = result; 808 setFolder(mConvListContext.folder); 809 showConversationList(mConvListContext); 810 mFetchAccountFolderTask = null; 811 } 812 } 813} 814