UIControllerOnePane.java revision ab40c988216b32ed145c0cad45c25e9cf2509c85
1/* 2 * Copyright (C) 2011 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 17package com.android.email.activity; 18 19import com.android.email.Email; 20import com.android.email.R; 21import com.android.email.activity.MailboxFinder.Callback; 22import com.android.email.activity.setup.AccountSecurity; 23import com.android.emailcommon.Logging; 24import com.android.emailcommon.provider.EmailContent.Account; 25import com.android.emailcommon.provider.EmailContent.Message; 26import com.android.emailcommon.provider.Mailbox; 27import com.android.emailcommon.utility.Utility; 28 29import android.app.Activity; 30import android.app.Fragment; 31import android.app.FragmentTransaction; 32import android.os.Bundle; 33import android.util.Log; 34import android.view.Menu; 35import android.view.MenuInflater; 36import android.view.MenuItem; 37 38import java.util.Set; 39 40 41/** 42 * UI Controller for non x-large devices. Supports a single-pane layout. 43 * 44 * One one-pane, multiple fragments can be installed at the same time, but only one of them 45 * can be "visible" at a time. Others are in the back stack. Use {@link #isMailboxListVisible()}, 46 * {@link #isMessageListVisible()} and {@link #isMessageViewVisible()} to determine which is 47 * visible. 48 * 49 * Note due to the asynchronous nature of the fragment transaction, there is a window when 50 * there is no installed or visible fragments. 51 * 52 * TODO Use the back stack for the message list -> message view navigation, so that the list 53 * position/selection will be restored on back. 54 * 55 * Major TODOs 56 * - TODO Newer/Older for message view with swipe! 57 * - TODO Implement callbacks 58 */ 59class UIControllerOnePane extends UIControllerBase { 60 // MailboxListFragment.Callback 61 @Override 62 public void onAccountSelected(long accountId) { 63 switchAccount(accountId); 64 } 65 66 // MailboxListFragment.Callback 67 @Override 68 public void onCurrentMailboxUpdated(long mailboxId, String mailboxName, int unreadCount) { 69 } 70 71 // MailboxListFragment.Callback 72 @Override 73 public void onMailboxSelected(long accountId, long mailboxId, boolean nestedNavigation) { 74 if (nestedNavigation) { 75 return; // Nothing to do on 1-pane. 76 } 77 openMailbox(accountId, mailboxId); 78 } 79 80 // MailboxListFragment.Callback 81 @Override 82 public void onParentMailboxChanged() { 83 refreshActionBar(); 84 } 85 86 // MessageListFragment.Callback 87 @Override 88 public void onAdvancingOpAccepted(Set<Long> affectedMessages) { 89 // Nothing to do on 1 pane. 90 } 91 92 // MessageListFragment.Callback 93 @Override 94 public void onEnterSelectionMode(boolean enter) { 95 // TODO Auto-generated method stub 96 } 97 98 // MessageListFragment.Callback 99 @Override 100 public void onListLoaded() { 101 // TODO Auto-generated method stub 102 } 103 104 // MessageListFragment.Callback 105 @Override 106 public void onMailboxNotFound() { 107 open(getUIAccountId(), Mailbox.NO_MAILBOX, Message.NO_MESSAGE); 108 } 109 110 // MessageListFragment.Callback 111 @Override 112 public void onMessageOpen( 113 long messageId, long messageMailboxId, long listMailboxId, int type) { 114 if (type == MessageListFragment.Callback.TYPE_DRAFT) { 115 MessageCompose.actionEditDraft(mActivity, messageId); 116 } else { 117 open(getUIAccountId(), getMailboxId(), messageId); 118 } 119 } 120 121 // MessageListFragment.Callback 122 @Override 123 public boolean onDragStarted() { 124 // No drag&drop on 1-pane 125 return false; 126 } 127 128 // MessageListFragment.Callback 129 @Override 130 public void onDragEnded() { 131 // No drag&drop on 1-pane 132 } 133 134 // MessageViewFragment.Callback 135 @Override 136 public void onForward() { 137 MessageCompose.actionForward(mActivity, getMessageId()); 138 } 139 140 // MessageViewFragment.Callback 141 @Override 142 public void onReply() { 143 MessageCompose.actionReply(mActivity, getMessageId(), false); 144 } 145 146 // MessageViewFragment.Callback 147 @Override 148 public void onReplyAll() { 149 MessageCompose.actionReply(mActivity, getMessageId(), true); 150 } 151 152 // MessageViewFragment.Callback 153 @Override 154 public void onCalendarLinkClicked(long epochEventStartTime) { 155 ActivityHelper.openCalendar(mActivity, epochEventStartTime); 156 } 157 158 // MessageViewFragment.Callback 159 @Override 160 public boolean onUrlInMessageClicked(String url) { 161 return ActivityHelper.openUrlInMessage(mActivity, url, getActualAccountId()); 162 } 163 164 // MessageViewFragment.Callback 165 @Override 166 public void onBeforeMessageGone() { 167 // TODO Auto-generated method stub 168 } 169 170 // MessageViewFragment.Callback 171 @Override 172 public void onMessageSetUnread() { 173 // TODO Auto-generated method stub 174 } 175 176 // MessageViewFragment.Callback 177 @Override 178 public void onRespondedToInvite(int response) { 179 // TODO Auto-generated method stub 180 } 181 182 // MessageViewFragment.Callback 183 @Override 184 public void onLoadMessageError(String errorMessage) { 185 // TODO Auto-generated method stub 186 } 187 188 // MessageViewFragment.Callback 189 @Override 190 public void onLoadMessageFinished() { 191 // TODO Auto-generated method stub 192 } 193 194 // MessageViewFragment.Callback 195 @Override 196 public void onLoadMessageStarted() { 197 // TODO Auto-generated method stub 198 } 199 200 // MessageViewFragment.Callback 201 @Override 202 public void onMessageNotExists() { 203 // TODO Auto-generated method stub 204 } 205 206 // MessageViewFragment.Callback 207 @Override 208 public void onMessageShown() { 209 // TODO Auto-generated method stub 210 } 211 212 // This is all temporary as we'll have a different action bar controller for 1-pane. 213 private class ActionBarControllerCallback implements ActionBarController.Callback { 214 @Override 215 public boolean shouldShowMailboxName() { 216 return false; // no mailbox name/unread count. 217 } 218 219 @Override 220 public String getCurrentMailboxName() { 221 return null; // no mailbox name/unread count. 222 } 223 224 @Override 225 public int getCurrentMailboxUnreadCount() { 226 return 0; // no mailbox name/unread count. 227 } 228 229 @Override 230 public boolean shouldShowUp() { 231 return isMessageViewVisible() 232 || (isMailboxListVisible() && !getMailboxListFragment().isRoot()); 233 } 234 235 @Override 236 public long getUIAccountId() { 237 return UIControllerOnePane.this.getUIAccountId(); 238 } 239 240 @Override 241 public void onMailboxSelected(long mailboxId) { 242 if (mailboxId == Mailbox.NO_MAILBOX) { 243 showAllMailboxes(); 244 } else { 245 UIControllerOnePane.this.openMailbox(getUIAccountId(), mailboxId); 246 } 247 } 248 249 @Override 250 public boolean isAccountSelected() { 251 return UIControllerOnePane.this.isAccountSelected(); 252 } 253 254 @Override 255 public void onAccountSelected(long accountId) { 256 switchAccount(accountId); 257 } 258 259 @Override 260 public void onNoAccountsFound() { 261 Welcome.actionStart(mActivity); 262 mActivity.finish(); 263 } 264 } 265 266 public UIControllerOnePane(EmailActivity activity) { 267 super(activity); 268 } 269 270 @Override 271 protected ActionBarController createActionBarController(Activity activity) { 272 273 // For now, we just reuse the same action bar controller used for 2-pane. 274 // We may change it later. 275 276 return new ActionBarController(activity, activity.getLoaderManager(), 277 activity.getActionBar(), new ActionBarControllerCallback()); 278 } 279 280 @Override 281 public void onSaveInstanceState(Bundle outState) { 282 super.onSaveInstanceState(outState); 283 } 284 285 @Override 286 public void restoreInstanceState(Bundle savedInstanceState) { 287 super.restoreInstanceState(savedInstanceState); 288 } 289 290 @Override 291 public int getLayoutId() { 292 return R.layout.email_activity_one_pane; 293 } 294 295 @Override 296 public void onActivityViewReady() { 297 super.onActivityViewReady(); 298 } 299 300 @Override 301 public void onActivityCreated() { 302 super.onActivityCreated(); 303 } 304 305 @Override 306 public void onActivityResume() { 307 super.onActivityResume(); 308 refreshActionBar(); 309 } 310 311 /** @return true if a {@link MailboxListFragment} is installed and visible. */ 312 private final boolean isMailboxListVisible() { 313 return isMailboxListInstalled(); 314 } 315 316 /** @return true if a {@link MessageListFragment} is installed and visible. */ 317 private final boolean isMessageListVisible() { 318 return isMessageListInstalled(); 319 } 320 321 /** @return true if a {@link MessageViewFragment} is installed and visible. */ 322 private final boolean isMessageViewVisible() { 323 return isMessageViewInstalled(); 324 } 325 326 @Override 327 public long getUIAccountId() { 328 // Get it from the visible fragment. 329 if (isMailboxListVisible()) { 330 return getMailboxListFragment().getAccountId(); 331 } 332 if (isMessageListVisible()) { 333 return getMessageListFragment().getAccountId(); 334 } 335 if (isMessageViewVisible()) { 336 return getMessageViewFragment().getOpenerAccountId(); 337 } 338 return Account.NO_ACCOUNT; 339 } 340 341 private long getMailboxId() { 342 // Get it from the visible fragment. 343 if (isMessageListVisible()) { 344 return getMessageListFragment().getMailboxId(); 345 } 346 if (isMessageViewVisible()) { 347 return getMessageViewFragment().getOpenerMailboxId(); 348 } 349 return Mailbox.NO_MAILBOX; 350 } 351 352 private long getMessageId() { 353 // Get it from the visible fragment. 354 if (isMessageViewVisible()) { 355 return getMessageViewFragment().getMessageId(); 356 } 357 return Message.NO_MESSAGE; 358 } 359 360 private final MailboxFinder.Callback mInboxLookupCallback = new MailboxFinder.Callback() { 361 @Override 362 public void onMailboxFound(long accountId, long mailboxId) { 363 // Inbox found. 364 openMailbox(accountId, mailboxId); 365 } 366 367 @Override 368 public void onAccountNotFound() { 369 // Account removed? 370 Welcome.actionStart(mActivity); 371 } 372 373 @Override 374 public void onMailboxNotFound(long accountId) { 375 // Inbox not found?? 376 Welcome.actionStart(mActivity); 377 } 378 379 @Override 380 public void onAccountSecurityHold(long accountId) { 381 mActivity.startActivity(AccountSecurity.actionUpdateSecurityIntent(mActivity, accountId, 382 true)); 383 } 384 }; 385 386 @Override 387 protected Callback getInboxLookupCallback() { 388 return mInboxLookupCallback; 389 } 390 391 @Override 392 public boolean onBackPressed(boolean isSystemBackKey) { 393 if (isMessageViewVisible()) { 394 openMailbox(getMessageViewFragment().getOpenerAccountId(), 395 getMessageViewFragment().getOpenerMailboxId()); 396 return true; 397 } else if (isMailboxListVisible() && getMailboxListFragment().navigateUp()) { 398 return true; 399 } 400 return false; 401 } 402 403 @Override 404 public void open(final long accountId, final long mailboxId, final long messageId) { 405 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 406 Log.d(Logging.LOG_TAG, this + " open accountId=" + accountId 407 + " mailboxId=" + mailboxId + " messageId=" + messageId); 408 } 409 if (accountId == Account.NO_ACCOUNT) { 410 throw new IllegalArgumentException(); 411 } 412 413 if ((getUIAccountId() == accountId) && (getMailboxId() == mailboxId) 414 && (getMessageId() == messageId)) { 415 return; 416 } 417 418 if (messageId != Message.NO_MESSAGE) { 419 showMessageView(accountId, mailboxId, messageId); 420 } else if (mailboxId != Mailbox.NO_MAILBOX) { 421 showMessageList(accountId, mailboxId); 422 } else { 423 // Mailbox not specified. Open Inbox or Combined Inbox. 424 if (accountId == Account.ACCOUNT_ID_COMBINED_VIEW) { 425 showMessageList(accountId, Mailbox.QUERY_ALL_INBOXES); 426 } else { 427 startInboxLookup(accountId); 428 } 429 } 430 } 431 432 private void removeAllFragments(FragmentTransaction ft) { 433 if (isMailboxListInstalled()) { 434 removeMailboxListFragment(ft); 435 } 436 if (isMessageListInstalled()) { 437 removeMessageListFragment(ft); 438 } 439 if (isMessageViewInstalled()) { 440 removeMessageViewFragment(ft); 441 } 442 } 443 444 private void showMailboxList(long accountId, long mailboxId) { 445 showFragment(MailboxListFragment.newInstance(accountId, mailboxId, false)); 446 } 447 448 private void showMessageList(long accountId, long mailboxId) { 449 showFragment(MessageListFragment.newInstance(accountId, mailboxId)); 450 } 451 452 private void showMessageView(long accountId, long mailboxId, long messageId) { 453 showFragment(MessageViewFragment.newInstance(accountId, mailboxId, messageId)); 454 } 455 456 private void showFragment(Fragment fragment) { 457 final FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction(); 458 removeAllFragments(ft); 459 ft.add(R.id.fragment_placeholder, fragment); 460 commitFragmentTransaction(ft); 461 } 462 463 private void showAllMailboxes() { 464 if (!isAccountSelected()) { 465 return; // Can happen because of asynchronous fragment transactions. 466 } 467 // Don't use open(account, NO_MAILBOX, NO_MESSAGE). This is used to open the default 468 // view, which is Inbox on the message list. 469 showMailboxList(getUIAccountId(), Mailbox.NO_MAILBOX); 470 } 471 472 /* 473 * STOPSHIP Remove this -- see the base class method. 474 */ 475 @Override 476 public long getMailboxSettingsMailboxId() { 477 // Mailbox settings is still experimental, and doesn't have to work on the phone. 478 Utility.showToast(mActivity, "STOPSHIP: Mailbox settings not supported on 1 pane"); 479 return Mailbox.NO_MAILBOX; 480 } 481 482 /* 483 * STOPSHIP Remove this -- see the base class method. 484 */ 485 @Override 486 public long getSearchMailboxId() { 487 // Search is still experimental, and doesn't have to work on the phone. 488 Utility.showToast(mActivity, "STOPSHIP: Search not supported on 1 pane"); 489 return Mailbox.NO_MAILBOX; 490 } 491 492 @Override 493 protected boolean isRefreshEnabled() { 494 // Refreshable only when an actual account is selected, and message view isn't shown. 495 // (i.e. only available on the mailbox list or the message view, but not on the combined 496 // one) 497 return isActualAccountSelected() && !isMessageViewVisible(); 498 } 499 500 @Override 501 public void onRefresh() { 502 if (!isRefreshEnabled()) { 503 return; 504 } 505 if (isMessageListVisible()) { 506 mRefreshManager.refreshMessageList(getActualAccountId(), getMailboxId(), true); 507 } else { 508 mRefreshManager.refreshMailboxList(getActualAccountId()); 509 } 510 } 511 512 @Override 513 protected boolean isRefreshInProgress() { 514 if (!isRefreshEnabled()) { 515 return false; 516 } 517 if (isMessageListVisible()) { 518 return mRefreshManager.isMessageListRefreshing(getMailboxId()); 519 } else { 520 return mRefreshManager.isMailboxListRefreshing(getActualAccountId()); 521 } 522 } 523 524 @Override 525 public boolean onPrepareOptionsMenu(MenuInflater inflater, Menu menu) { 526 // STOPSHIP For temporary menu item which should be visible only on 1-pane. 527 menu.findItem(R.id.show_all_folders).setVisible(true); 528 return super.onPrepareOptionsMenu(inflater, menu); 529 } 530 531 @Override 532 public boolean onOptionsItemSelected(MenuItem item) { 533 switch (item.getItemId()) { 534 case R.id.show_all_folders: // STOPSHIP For temporary menu item 535 showAllMailboxes(); 536 return true; 537 } 538 return super.onOptionsItemSelected(item); 539 } 540} 541