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