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