UIControllerOnePane.java revision 844bf745044b4564f42a68f8b7d40105c4def294
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.emailcommon.Logging; 22import com.android.emailcommon.provider.EmailContent.Account; 23import com.android.emailcommon.provider.EmailContent.Message; 24import com.android.emailcommon.provider.Mailbox; 25import com.android.emailcommon.utility.Utility; 26 27import android.app.FragmentManager; 28import android.app.FragmentTransaction; 29import android.os.Bundle; 30import android.util.Log; 31 32import java.util.Set; 33 34 35/** 36 * UI Controller for non x-large devices. Supports a single-pane layout. 37 * 38 * STOPSHIP Everything in this class is 100% temporary at this point 39 * - Navigation model is different from what it should be (whatever it'll be). 40 * e.g. when the app is launched, we should show Inbox, not mailbox list. 41 * 42 * - It uses the two-pane action bar only so that we can change accounts 43 * 44 * Major TODOs 45 * - TODO Proper Navigation model, including retaining fragments to keep state such as the scroll 46 * position and batch selection. 47 * - TODO Nested folders 48 * - TODO Newer/Older for message view with swipe! 49 * - TODO Implement callbacks 50 */ 51class UIControllerOnePane extends UIControllerBase { 52 private ActionBarController mActionBarController; 53 54 /** 55 * Current account/mailbox/message IDs. 56 * Don't use them directly; use the accessors instead, as we might want to get them from the 57 * topmost fragment in the future. 58 */ 59 private long mCurrentAccountId = Account.NO_ACCOUNT; 60 private long mCurrentMailboxId = Mailbox.NO_MAILBOX; 61 private long mCurrentMessageId = Message.NO_MESSAGE; 62 63 private MessageCommandButtonView mMessageCommandButtons; 64 65 private final MailboxListFragment.Callback mMailboxListFragmentCallback = 66 new MailboxListFragment.Callback() { 67 @Override 68 public void onAccountSelected(long accountId) { 69 switchAccount(accountId); 70 } 71 72 @Override 73 public void onCurrentMailboxUpdated(long mailboxId, String mailboxName, int unreadCount) { 74 } 75 76 @Override 77 public void onMailboxSelected(long accountId, long mailboxId, boolean nestedNavigation) { 78 if (nestedNavigation) { 79 return; // Nothing to do on 1-pane. 80 } 81 openMailbox(accountId, mailboxId); 82 } 83 84 @Override 85 public void onParentMailboxChanged() { 86 refreshActionBar(); 87 } 88 }; 89 90 private final MessageListFragment.Callback mMessageListFragmentCallback = 91 new MessageListFragment.Callback() { 92 @Override 93 public void onAdvancingOpAccepted(Set<Long> affectedMessages) { 94 // Nothing to do on 1 pane. 95 } 96 97 @Override 98 public void onEnterSelectionMode(boolean enter) { 99 // TODO Auto-generated method stub 100 } 101 102 @Override 103 public void onListLoaded() { 104 // TODO Auto-generated method stub 105 106 } 107 108 @Override 109 public void onMailboxNotFound() { 110 open(getUIAccountId(), Mailbox.NO_MAILBOX, Message.NO_MESSAGE); 111 } 112 113 @Override 114 public void onMessageOpen( 115 long messageId, long messageMailboxId, long listMailboxId, int type) { 116 if (type == MessageListFragment.Callback.TYPE_DRAFT) { 117 MessageCompose.actionEditDraft(mActivity, messageId); 118 } else { 119 open(getUIAccountId(), getMailboxId(), messageId); 120 } 121 } 122 123 @Override 124 public boolean onDragStarted() { 125 // No drag&drop on 1-pane 126 return false; 127 } 128 129 @Override 130 public void onDragEnded() { 131 // No drag&drop on 1-pane 132 } 133 }; 134 135 private final MessageViewFragment.Callback mMessageViewFragmentCallback = 136 new MessageViewFragment.Callback() { 137 @Override 138 public void onForward() { 139 MessageCompose.actionForward(mActivity, getMessageId()); 140 } 141 142 @Override 143 public void onReply() { 144 MessageCompose.actionReply(mActivity, getMessageId(), false); 145 } 146 147 @Override 148 public void onReplyAll() { 149 MessageCompose.actionReply(mActivity, getMessageId(), true); 150 } 151 152 @Override 153 public void onCalendarLinkClicked(long epochEventStartTime) { 154 ActivityHelper.openCalendar(mActivity, epochEventStartTime); 155 } 156 157 @Override 158 public boolean onUrlInMessageClicked(String url) { 159 return ActivityHelper.openUrlInMessage(mActivity, url, getActualAccountId()); 160 } 161 162 @Override 163 public void onBeforeMessageGone() { 164 // TODO Auto-generated method stub 165 166 } 167 168 @Override 169 public void onMessageSetUnread() { 170 // TODO Auto-generated method stub 171 172 } 173 174 @Override 175 public void onRespondedToInvite(int response) { 176 // TODO Auto-generated method stub 177 178 } 179 180 @Override 181 public void onLoadMessageError(String errorMessage) { 182 // TODO Auto-generated method stub 183 184 } 185 186 @Override 187 public void onLoadMessageFinished() { 188 // TODO Auto-generated method stub 189 190 } 191 192 @Override 193 public void onLoadMessageStarted() { 194 // TODO Auto-generated method stub 195 196 } 197 198 @Override 199 public void onMessageNotExists() { 200 // TODO Auto-generated method stub 201 202 } 203 204 205 @Override 206 public void onMessageShown() { 207 // TODO Auto-generated method stub 208 209 } 210 }; 211 212 // This is all temporary as we'll have a different action bar controller for 1-pane. 213 private final ActionBarController.Callback mActionBarControllerCallback 214 = new ActionBarController.Callback() { 215 @Override 216 public boolean shouldShowMailboxName() { 217 return false; // no mailbox name/unread count. 218 } 219 220 @Override 221 public String getCurrentMailboxName() { 222 return null; // no mailbox name/unread count. 223 } 224 225 @Override 226 public int getCurrentMailboxUnreadCount() { 227 return 0; // no mailbox name/unread count. 228 } 229 230 @Override 231 public boolean shouldShowUp() { 232 // Always show the UP arrow. 233 return true; 234 } 235 236 @Override 237 public long getUIAccountId() { 238 return UIControllerOnePane.this.getUIAccountId(); 239 } 240 241 @Override 242 public boolean isAccountSelected() { 243 return UIControllerOnePane.this.isAccountSelected(); 244 } 245 246 @Override 247 public void onAccountSelected(long accountId) { 248 switchAccount(accountId); 249 } 250 251 @Override 252 public void onNoAccountsFound() { 253 Welcome.actionStart(mActivity); 254 mActivity.finish(); 255 } 256 }; 257 258 public UIControllerOnePane(EmailActivity activity) { 259 super(activity); 260 } 261 262 @Override 263 public void onSaveInstanceState(Bundle outState) { 264 super.onSaveInstanceState(outState); 265 outState.putLong(BUNDLE_KEY_ACCOUNT_ID, mCurrentAccountId); 266 outState.putLong(BUNDLE_KEY_MAILBOX_ID, mCurrentMailboxId); 267 outState.putLong(BUNDLE_KEY_MESSAGE_ID, mCurrentMessageId); 268 } 269 270 @Override 271 public void restoreInstanceState(Bundle savedInstanceState) { 272 super.restoreInstanceState(savedInstanceState); 273 mCurrentAccountId = savedInstanceState.getLong(BUNDLE_KEY_ACCOUNT_ID, Account.NO_ACCOUNT); 274 mCurrentMailboxId = savedInstanceState.getLong(BUNDLE_KEY_MAILBOX_ID, Mailbox.NO_MAILBOX); 275 mCurrentMessageId = savedInstanceState.getLong(BUNDLE_KEY_MESSAGE_ID, Message.NO_MESSAGE); 276 } 277 278 @Override 279 public int getLayoutId() { 280 return R.layout.email_activity_one_pane; 281 } 282 283 @Override 284 public void onActivityViewReady() { 285 super.onActivityViewReady(); 286 mActionBarController = new ActionBarController(mActivity, mActivity.getLoaderManager(), 287 mActivity.getActionBar(), mActionBarControllerCallback); 288 289 mMessageCommandButtons = UiUtilities.getView(mActivity, R.id.message_command_buttons); 290 mMessageCommandButtons.setCallback(new CommandButtonCallback()); 291 } 292 293 @Override 294 public void onActivityCreated() { 295 super.onActivityCreated(); 296 mActionBarController.onActivityCreated(); 297 } 298 299 @Override 300 public void onActivityResume() { 301 super.onActivityResume(); 302 refreshActionBar(); 303 } 304 305 @Override 306 public long getUIAccountId() { 307 return mCurrentAccountId; 308 } 309 310 private long getMailboxId() { 311 return mCurrentMailboxId; 312 } 313 314 private long getMessageId() { 315 return mCurrentMessageId; 316 } 317 318 private void refreshActionBar() { 319 if (mActionBarController != null) { 320 mActionBarController.refresh(); 321 } 322 mActivity.invalidateOptionsMenu(); 323 } 324 325 private boolean isMailboxListVisible() { 326 return (getMailboxId() == Mailbox.NO_MAILBOX); 327 } 328 329 private boolean isMessageListVisible() { 330 return (getMailboxId() != Mailbox.NO_MAILBOX) && (getMessageId() == Message.NO_MESSAGE); 331 } 332 333 private boolean isMessageViewVisible() { 334 return (getMailboxId() != Mailbox.NO_MAILBOX) && (getMessageId() != Message.NO_MESSAGE); 335 } 336 337 @Override 338 public boolean onBackPressed(boolean isSystemBackKey) { 339 if (isMessageViewVisible()) { 340 open(getUIAccountId(), getMailboxId(), Message.NO_MESSAGE); 341 return true; 342 } else if (isMessageListVisible()) { 343 open(getUIAccountId(), Mailbox.NO_MAILBOX, Message.NO_MESSAGE); 344 return true; 345 } else { 346 // TODO Call MailboxListFragment.navigateUp(). 347 348 // STOPSHIP Remove this and return false. This is so that the app can be closed 349 // with the UP press. (usuful when the device doesn't have a HW back key.) 350 mActivity.finish(); 351 return true; 352// return false; 353 } 354 } 355 356 @Override 357 protected void installMailboxListFragment(MailboxListFragment fragment) { 358 fragment.setCallback(mMailboxListFragmentCallback); 359 } 360 361 @Override 362 protected void installMessageListFragment(MessageListFragment fragment) { 363 fragment.setCallback(mMessageListFragmentCallback); 364 } 365 366 @Override 367 protected void installMessageViewFragment(MessageViewFragment fragment) { 368 fragment.setCallback(mMessageViewFragmentCallback); 369 } 370 371 @Override 372 public void open(final long accountId, final long mailboxId, final long messageId) { 373 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 374 Log.d(Logging.LOG_TAG, this + " open accountId=" + accountId 375 + " mailboxId=" + mailboxId + " messageId=" + messageId); 376 } 377 if (accountId == Account.NO_ACCOUNT) { 378 throw new IllegalArgumentException(); 379 } 380 381 // !!! It's all temporary to make 1 pane UI (barely) usable !!! 382 // 383 // - Nested folders still doesn't work 384 // - When opening a child view (e.g. message list -> message view), we should retain 385 // the current fragment so that all the state (selection, scroll position, etc) will be 386 // restored when back. 387 388 if ((getUIAccountId() == accountId) && (getMailboxId() == mailboxId) 389 && (getMessageId() == messageId)) { 390 return; 391 } 392 393 final FragmentManager fm = mActivity.getFragmentManager(); 394 final FragmentTransaction ft = fm.beginTransaction(); 395 396 if (messageId != Message.NO_MESSAGE) { 397 ft.replace(R.id.fragment_placeholder, MessageViewFragment.newInstance(messageId)); 398 399 } else if (mailboxId != Mailbox.NO_MAILBOX) { 400 ft.replace(R.id.fragment_placeholder, MessageListFragment.newInstance( 401 accountId, mailboxId)); 402 403 } else { 404 ft.replace(R.id.fragment_placeholder, 405 MailboxListFragment.newInstance(accountId, Mailbox.NO_MAILBOX, false)); 406 } 407 408 mCurrentAccountId = accountId; 409 mCurrentMailboxId = mailboxId; 410 mCurrentMessageId = messageId; 411 412 commitFragmentTransaction(ft); 413 414 refreshActionBar(); 415 } 416 417 /* 418 * STOPSHIP Remove this -- see the base class method. 419 */ 420 @Override 421 public long getMailboxSettingsMailboxId() { 422 // Mailbox settings is still experimental, and doesn't have to work on the phone. 423 Utility.showToast(mActivity, "STOPSHIP: Mailbox settings not supported on 1 pane"); 424 return Mailbox.NO_MAILBOX; 425 } 426 427 /* 428 * STOPSHIP Remove this -- see the base class method. 429 */ 430 @Override 431 public long getSearchMailboxId() { 432 // Search is still experimental, and doesn't have to work on the phone. 433 Utility.showToast(mActivity, "STOPSHIP: Search not supported on 1 pane"); 434 return Mailbox.NO_MAILBOX; 435 } 436 437 private class CommandButtonCallback implements MessageCommandButtonView.Callback { 438 @Override 439 public void onMoveToNewer() { 440 // TODO 441 } 442 443 @Override 444 public void onMoveToOlder() { 445 // TODO 446 } 447 } 448 449 @Override 450 protected boolean isRefreshEnabled() { 451 // Refreshable only when an actual account is selected, and message view isn't shown. 452 // (i.e. only available on the mailbox list or the message view, but not on the combined 453 // one) 454 return isActualAccountSelected() && !isMessageViewVisible(); 455 } 456 457 @Override 458 public void onRefresh() { 459 if (!isRefreshEnabled()) { 460 return; 461 } 462 if (isMessageListVisible()) { 463 mRefreshManager.refreshMessageList(getActualAccountId(), getMailboxId(), true); 464 } else { 465 mRefreshManager.refreshMailboxList(getActualAccountId()); 466 } 467 } 468 469 @Override 470 protected boolean isRefreshInProgress() { 471 if (!isRefreshEnabled()) { 472 return false; 473 } 474 if (isMessageListVisible()) { 475 return mRefreshManager.isMessageListRefreshing(getMailboxId()); 476 } else { 477 return mRefreshManager.isMailboxListRefreshing(getActualAccountId()); 478 } 479 } 480} 481