MailboxListFragment.java revision 0698f4027f099a017ad7815f16572ba422d3413d
1/* 2 * Copyright (C) 2010 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.Controller; 20import com.android.email.Email; 21import com.android.email.R; 22import com.android.email.RefreshManager; 23import com.android.email.Utility; 24import com.android.email.provider.EmailProvider; 25import com.android.email.provider.EmailContent.Mailbox; 26import com.android.email.provider.EmailContent.Message; 27 28import android.app.Activity; 29import android.app.ListFragment; 30import android.app.LoaderManager.LoaderCallbacks; 31import android.content.ClipData; 32import android.content.ClipDescription; 33import android.content.Loader; 34import android.database.Cursor; 35import android.net.Uri; 36import android.os.Bundle; 37import android.util.Log; 38import android.view.DragEvent; 39import android.view.LayoutInflater; 40import android.view.View; 41import android.view.View.OnDragListener; 42import android.view.ViewGroup; 43import android.widget.AdapterView; 44import android.widget.ListView; 45import android.widget.AdapterView.OnItemClickListener; 46 47import java.security.InvalidParameterException; 48 49/** 50 * This fragment presents a list of mailboxes for a given account. The "API" includes the 51 * following elements which must be provided by the host Activity. 52 * 53 * - call bindActivityInfo() to provide the account ID and set callbacks 54 * - provide callbacks for onOpen and onRefresh 55 * - pass-through implementations of onCreateContextMenu() and onContextItemSelected() (temporary) 56 * 57 * TODO Restoring ListView state -- don't do this when changing accounts 58 */ 59public class MailboxListFragment extends ListFragment implements OnItemClickListener, 60 OnDragListener { 61 private static final String TAG = "MailboxListFragment"; 62 private static final String BUNDLE_KEY_SELECTED_MAILBOX_ID 63 = "MailboxListFragment.state.selected_mailbox_id"; 64 private static final String BUNDLE_LIST_STATE = "MailboxListFragment.state.listState"; 65 private static final int LOADER_ID_MAILBOX_LIST = 1; 66 private static final boolean DEBUG_DRAG_DROP = false; // MUST NOT SUBMIT SET TO TRUE 67 68 private static final int NO_DROP_TARGET = -1; 69 // Top and bottom scroll zone size, in pixels 70 private static final int SCROLL_ZONE_SIZE = 64; 71 72 private long mLastLoadedAccountId = -1; 73 private long mAccountId = -1; 74 private long mSelectedMailboxId = -1; 75 76 private RefreshManager mRefreshManager; 77 78 // UI Support 79 private Activity mActivity; 80 private MailboxesAdapter mListAdapter; 81 private Callback mCallback = EmptyCallback.INSTANCE; 82 83 private ListView mListView; 84 85 private boolean mOpenRequested; 86 private boolean mResumed; 87 88 // Color used when we're dragging over a valid drop target 89 private static final int DROP_TARGET = 0xFFFFCC33; 90 // Color used when we're dragging over a "delete" target (i.e. the trash mailbox) 91 private static final int DROP_TARGET_TRASH = 0xFFFF0000; 92 93 // True if a drag is currently in progress 94 private boolean mDragInProgress = false; 95 // The mailbox id of the dragged item's mailbox. We use it to prevent that box from being a 96 // valid drop target 97 private long mDragItemMailboxId = -1; 98 // The adapter position that the user's finger is hovering over 99 private int mDropTargetAdapterPosition = NO_DROP_TARGET; 100 // The mailbox list item view that the user's finger is hovering over 101 private MailboxListItem mDropTargetView; 102 // Lazily instantiated height of a mailbox list item (-1 is a sentinel for 'not initialized') 103 private int mDragItemHeight = -1; 104 // STOPSHIP Scrolling related code is preliminary and will likely be completely replaced 105 // True if we are currently scrolling under the drag item 106 private boolean mTargetScrolling = false; 107 private int mScrollSpeed = -1; 108 109 private Utility.ListStateSaver mSavedListState; 110 111 private MailboxesAdapter.Callback mMailboxesAdapterCallback = new MailboxesAdapter.Callback() { 112 @Override 113 public void onSetDropTargetBackground(MailboxListItem listItem) { 114 listItem.setDropTargetBackground(mDragInProgress, mDragItemMailboxId); 115 } 116 }; 117 118 /** 119 * Callback interface that owning activities must implement 120 */ 121 public interface Callback { 122 /** Called when a mailbox (including combined mailbox) is selected. */ 123 public void onMailboxSelected(long accountId, long mailboxId); 124 125 /** Called when an account is selected on the combined view. */ 126 public void onAccountSelected(long accountId); 127 128 /** 129 * Called when the list updates to propagate the current mailbox name and the unread count 130 * for it. 131 * 132 * Note the reason why it's separated from onMailboxSelected is because this needs to be 133 * reported when the unread count changes without changing the current mailbox. 134 */ 135 public void onCurrentMailboxUpdated(long mailboxId, String mailboxName, int unreadCount); 136 } 137 138 private static class EmptyCallback implements Callback { 139 public static final Callback INSTANCE = new EmptyCallback(); 140 @Override public void onMailboxSelected(long accountId, long mailboxId) { } 141 @Override public void onAccountSelected(long accountId) { } 142 @Override public void onCurrentMailboxUpdated(long mailboxId, String mailboxName, 143 int unreadCount) { } 144 } 145 146 /** 147 * Called to do initial creation of a fragment. This is called after 148 * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}. 149 */ 150 @Override 151 public void onCreate(Bundle savedInstanceState) { 152 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 153 Log.d(Email.LOG_TAG, "MailboxListFragment onCreate"); 154 } 155 super.onCreate(savedInstanceState); 156 157 mActivity = getActivity(); 158 mRefreshManager = RefreshManager.getInstance(mActivity); 159 mListAdapter = new MailboxesAdapter(mActivity, MailboxesAdapter.MODE_NORMAL, 160 mMailboxesAdapterCallback); 161 if (savedInstanceState != null) { 162 restoreInstanceState(savedInstanceState); 163 } 164 } 165 166 @Override 167 public View onCreateView( 168 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 169 return inflater.inflate(R.layout.mailbox_list_fragment, container, false); 170 } 171 172 @Override 173 public void onActivityCreated(Bundle savedInstanceState) { 174 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 175 Log.d(Email.LOG_TAG, "MailboxListFragment onActivityCreated"); 176 } 177 super.onActivityCreated(savedInstanceState); 178 179 mListView = getListView(); 180 mListView.setOnItemClickListener(this); 181 mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 182 mListView.setOnDragListener(this); 183 registerForContextMenu(mListView); 184 } 185 186 public void setCallback(Callback callback) { 187 mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback; 188 } 189 190 /** 191 * @param accountId the account we're looking at 192 */ 193 public void openMailboxes(long accountId) { 194 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 195 Log.d(Email.LOG_TAG, "MailboxListFragment openMailboxes"); 196 } 197 if (accountId == -1) { 198 throw new InvalidParameterException(); 199 } 200 if (mAccountId == accountId) { 201 return; 202 } 203 mOpenRequested = true; 204 mAccountId = accountId; 205 if (mResumed) { 206 startLoading(); 207 } 208 } 209 210 public void setSelectedMailbox(long mailboxId) { 211 mSelectedMailboxId = mailboxId; 212 if (mResumed) { 213 highlightSelectedMailbox(true); 214 } 215 } 216 217 /** 218 * Called when the Fragment is visible to the user. 219 */ 220 @Override 221 public void onStart() { 222 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 223 Log.d(Email.LOG_TAG, "MailboxListFragment onStart"); 224 } 225 super.onStart(); 226 } 227 228 /** 229 * Called when the fragment is visible to the user and actively running. 230 */ 231 @Override 232 public void onResume() { 233 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 234 Log.d(Email.LOG_TAG, "MailboxListFragment onResume"); 235 } 236 super.onResume(); 237 mResumed = true; 238 239 // If we're recovering from the stopped state, we don't have to reload. 240 // (when mOpenRequested = false) 241 if (mAccountId != -1 && mOpenRequested) { 242 startLoading(); 243 } 244 } 245 246 @Override 247 public void onPause() { 248 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 249 Log.d(Email.LOG_TAG, "MailboxListFragment onPause"); 250 } 251 mResumed = false; 252 super.onPause(); 253 mSavedListState = new Utility.ListStateSaver(getListView()); 254 } 255 256 /** 257 * Called when the Fragment is no longer started. 258 */ 259 @Override 260 public void onStop() { 261 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 262 Log.d(Email.LOG_TAG, "MailboxListFragment onStop"); 263 } 264 super.onStop(); 265 } 266 267 /** 268 * Called when the fragment is no longer in use. 269 */ 270 @Override 271 public void onDestroy() { 272 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 273 Log.d(Email.LOG_TAG, "MailboxListFragment onDestroy"); 274 } 275 super.onDestroy(); 276 } 277 278 @Override 279 public void onSaveInstanceState(Bundle outState) { 280 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 281 Log.d(Email.LOG_TAG, "MailboxListFragment onSaveInstanceState"); 282 } 283 super.onSaveInstanceState(outState); 284 outState.putLong(BUNDLE_KEY_SELECTED_MAILBOX_ID, mSelectedMailboxId); 285 outState.putParcelable(BUNDLE_LIST_STATE, new Utility.ListStateSaver(getListView())); 286 } 287 288 private void restoreInstanceState(Bundle savedInstanceState) { 289 mSelectedMailboxId = savedInstanceState.getLong(BUNDLE_KEY_SELECTED_MAILBOX_ID); 290 mSavedListState = savedInstanceState.getParcelable(BUNDLE_LIST_STATE); 291 } 292 293 private void startLoading() { 294 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 295 Log.d(Email.LOG_TAG, "MailboxListFragment startLoading"); 296 } 297 mOpenRequested = false; 298 // Clear the list. (ListFragment will show the "Loading" animation) 299 setListShown(false); 300 301 // If we've already loaded for a different account, discard the previous result and 302 // start loading again. 303 // We don't want to use restartLoader(), because if the Loader is retained, we *do* want to 304 // reuse the previous result. 305 // Also, when changing accounts, we don't preserve scroll position. 306 boolean accountChanging = false; 307 if ((mLastLoadedAccountId != -1) && (mLastLoadedAccountId != mAccountId)) { 308 accountChanging = true; 309 getLoaderManager().destroyLoader(LOADER_ID_MAILBOX_LIST); 310 311 // Also, when we're changing account, update the mailbox list if stale. 312 refreshMailboxListIfStale(); 313 } 314 getLoaderManager().initLoader(LOADER_ID_MAILBOX_LIST, null, 315 new MailboxListLoaderCallbacks(accountChanging)); 316 } 317 318 private class MailboxListLoaderCallbacks implements LoaderCallbacks<Cursor> { 319 private boolean mAccountChanging; 320 321 public MailboxListLoaderCallbacks(boolean accountChanging) { 322 mAccountChanging = accountChanging; 323 } 324 325 @Override 326 public Loader<Cursor> onCreateLoader(int id, Bundle args) { 327 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 328 Log.d(Email.LOG_TAG, "MailboxListFragment onCreateLoader"); 329 } 330 return MailboxesAdapter.createLoader(getActivity(), mAccountId, 331 MailboxesAdapter.MODE_NORMAL); 332 } 333 334 @Override 335 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { 336 if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { 337 Log.d(Email.LOG_TAG, "MailboxListFragment onLoadFinished"); 338 } 339 mLastLoadedAccountId = mAccountId; 340 341 // Save list view state (primarily scroll position) 342 final ListView lv = getListView(); 343 final Utility.ListStateSaver lss; 344 if (mAccountChanging) { 345 lss = null; // Don't preserve list state 346 } else if (mSavedListState != null) { 347 lss = mSavedListState; 348 mSavedListState = null; 349 } else { 350 lss = new Utility.ListStateSaver(lv); 351 } 352 353 if (cursor.getCount() == 0) { 354 // If there's no row, don't set it to the ListView. 355 // Instead use setListShown(false) to make ListFragment show progress icon. 356 mListAdapter.swapCursor(null); 357 setListShown(false); 358 } else { 359 // Set the adapter. 360 mListAdapter.swapCursor(cursor); 361 setListAdapter(mListAdapter); 362 setListShown(true); 363 364 // We want to make selection visible only when account is changing.. 365 // i.e. Refresh caused by content changed events shouldn't scroll the list. 366 highlightSelectedMailbox(mAccountChanging); 367 } 368 369 // Restore the state 370 if (lss != null) { 371 lss.restore(lv); 372 } 373 374 // Clear this for next reload triggered by content changed events. 375 mAccountChanging = false; 376 } 377 378 @Override 379 public void onLoaderReset(Loader<Cursor> loader) { 380 mListAdapter.swapCursor(null); 381 } 382 } 383 384 public void onItemClick(AdapterView<?> parent, View view, int position, 385 long idDontUseIt /* see MailboxesAdapter */ ) { 386 final long id = mListAdapter.getId(position); 387 if (mListAdapter.isAccountRow(position)) { 388 mCallback.onAccountSelected(id); 389 } else { 390 mCallback.onMailboxSelected(mAccountId, id); 391 } 392 } 393 394 public void onRefresh() { 395 if (mAccountId != -1) { 396 mRefreshManager.refreshMailboxList(mAccountId); 397 } 398 } 399 400 private void refreshMailboxListIfStale() { 401 if (mRefreshManager.isMailboxListStale(mAccountId)) { 402 mRefreshManager.refreshMailboxList(mAccountId); 403 } 404 } 405 406 /** 407 * Highlight the selected mailbox. 408 */ 409 private void highlightSelectedMailbox(boolean ensureSelectionVisible) { 410 String mailboxName = ""; 411 int unreadCount = 0; 412 if (mSelectedMailboxId == -1) { 413 // No mailbox selected 414 mListView.clearChoices(); 415 } else { 416 final int count = mListView.getCount(); 417 for (int i = 0; i < count; i++) { 418 if (mListAdapter.getId(i) != mSelectedMailboxId) { 419 continue; 420 } 421 mListView.setItemChecked(i, true); 422 if (ensureSelectionVisible) { 423 Utility.listViewSmoothScrollToPosition(getActivity(), mListView, i); 424 } 425 mailboxName = mListAdapter.getDisplayName(i); 426 unreadCount = mListAdapter.getUnreadCount(i); 427 break; 428 } 429 } 430 mCallback.onCurrentMailboxUpdated(mSelectedMailboxId, mailboxName, unreadCount); 431 } 432 433 // Drag & Drop handling 434 435 /** 436 * Update all of the list's child views with the proper target background (for now, orange if 437 * a valid target, except red if the trash; standard background otherwise) 438 */ 439 private void updateChildViews() { 440 int itemCount = mListView.getChildCount(); 441 // Lazily initialize the height of our list items 442 if (itemCount > 0 && mDragItemHeight < 0) { 443 mDragItemHeight = mListView.getChildAt(0).getHeight(); 444 } 445 for (int i = 0; i < itemCount; i++) { 446 MailboxListItem item = (MailboxListItem)mListView.getChildAt(i); 447 item.setDropTargetBackground(mDragInProgress, mDragItemMailboxId); 448 } 449 } 450 451 /** 452 * Called when our ListView gets a DRAG_EXITED event 453 */ 454 private void onDragExited() { 455 // Reset the background of the current target 456 if (mDropTargetAdapterPosition != NO_DROP_TARGET) { 457 mDropTargetView.setDropTargetBackground(mDragInProgress, mDragItemMailboxId); 458 mDropTargetAdapterPosition = NO_DROP_TARGET; 459 } 460 stopScrolling(); 461 } 462 463 /** 464 * STOPSHIP: Very preliminary scrolling code 465 */ 466 private void onDragLocation(DragEvent event) { 467 // The drag is somewhere in the ListView 468 if (mDragItemHeight <= 0) { 469 // This shouldn't be possible, but avoid NPE 470 return; 471 } 472 // Find out which item we're in and highlight as appropriate 473 int rawTouchY = (int)event.getY(); 474 int offset = 0; 475 if (mListView.getCount() > 0) { 476 offset = mListView.getChildAt(0).getTop(); 477 } 478 int targetScreenPosition = (rawTouchY - offset) / mDragItemHeight; 479 int firstVisibleItem = mListView.getFirstVisiblePosition(); 480 int targetAdapterPosition = firstVisibleItem + targetScreenPosition; 481 if (targetAdapterPosition != mDropTargetAdapterPosition) { 482 if (DEBUG_DRAG_DROP) { 483 Log.d(TAG, "========== DROP TARGET " + mDropTargetAdapterPosition + " -> " + 484 targetAdapterPosition); 485 } 486 // Unhighlight the current target, if we've got one 487 if (mDropTargetAdapterPosition != NO_DROP_TARGET) { 488 mDropTargetView.setDropTargetBackground(true, mDragItemMailboxId); 489 } 490 // Get the new target mailbox view 491 MailboxListItem newTarget = 492 (MailboxListItem)mListView.getChildAt(targetScreenPosition); 493 // This can be null due to a bug in the framework (checking on that) 494 // In any event, we're no longer dragging in the list view if newTarget is null 495 if (newTarget == null) { 496 if (DEBUG_DRAG_DROP) { 497 Log.d(TAG, "========== WTF??? DRAG EXITED"); 498 } 499 onDragExited(); 500 return; 501 } else if (newTarget.mMailboxType == Mailbox.TYPE_TRASH) { 502 Log.d("onDragLocation", "=== Mailbox " + newTarget.mMailboxId + " TRASH"); 503 newTarget.setBackgroundColor(DROP_TARGET_TRASH); 504 } else if (newTarget.isDropTarget(mDragItemMailboxId)) { 505 Log.d("onDragLocation", "=== Mailbox " + newTarget.mMailboxId + " TARGET"); 506 newTarget.setBackgroundColor(DROP_TARGET); 507 } else { 508 Log.d("onDragLocation", "=== Mailbox " + newTarget.mMailboxId + " (CALL)"); 509 targetAdapterPosition = NO_DROP_TARGET; 510 newTarget.setDropTargetBackground(true, mDragItemMailboxId); 511 } 512 // Save away our current position and view 513 mDropTargetAdapterPosition = targetAdapterPosition; 514 mDropTargetView = newTarget; 515 } 516 517 // STOPSHIP 518 // This is a quick-and-dirty implementation of drag-under-scroll. To be rewritten 519 int scrollDiff = rawTouchY - (mListView.getHeight() - SCROLL_ZONE_SIZE); 520 boolean scrollDown = false; 521 boolean scrollUp = false; 522 // 1 to 4 523 int scrollSpeed = 0; 524 if (scrollDiff > 0) { 525 scrollDown = true; 526 scrollSpeed = (scrollDiff / SCROLL_ZONE_SIZE / 4) + 1; 527 } else { 528 scrollDiff = SCROLL_ZONE_SIZE - rawTouchY; 529 if (scrollDiff > 0) { 530 scrollUp = true; 531 scrollSpeed = (scrollDiff / SCROLL_ZONE_SIZE / 4) + 1; 532 } 533 } 534 if ((!mTargetScrolling || (mScrollSpeed != scrollSpeed)) && scrollDown) { 535 mScrollSpeed = scrollSpeed; 536 int itemsToScroll = mListView.getCount() - targetAdapterPosition; 537 int pixelsToScroll = (itemsToScroll+1)*mDragItemHeight; 538 mListView.smoothScrollBy(pixelsToScroll, pixelsToScroll*16/scrollSpeed/4); 539 if (DEBUG_DRAG_DROP) { 540 Log.d(TAG, "========== START TARGET SCROLLING DOWN"); 541 } 542 mTargetScrolling = true; 543 } else if ((!mTargetScrolling || mScrollSpeed != scrollSpeed) && scrollUp) { 544 mScrollSpeed = scrollSpeed; 545 int pixelsToScroll = (firstVisibleItem+1)*mDragItemHeight; 546 mListView.smoothScrollBy(-pixelsToScroll, pixelsToScroll*16/scrollSpeed/4); 547 if (DEBUG_DRAG_DROP) { 548 Log.d(TAG, "========== START TARGET SCROLLING UP"); 549 } 550 mTargetScrolling = true; 551 } else if (!scrollUp && !scrollDown) { 552 stopScrolling(); 553 } 554 } 555 556 /** 557 * Indicate that scrolling has stopped 558 */ 559 private void stopScrolling() { 560 if (mTargetScrolling) { 561 mTargetScrolling = false; 562 if (DEBUG_DRAG_DROP) { 563 Log.d(TAG, "========== STOP TARGET SCROLLING"); 564 } 565 // Stop the scrolling 566 mListView.smoothScrollBy(0, 0); 567 } 568 } 569 570 private void onDragEnded() { 571 if (mDragInProgress) { 572 mDragInProgress = false; 573 // Reenable updates to the view and redraw (in case it changed) 574 mListAdapter.enableUpdates(true); 575 mListAdapter.notifyDataSetChanged(); 576 // Stop highlighting targets 577 updateChildViews(); 578 // Stop any scrolling that was going on 579 stopScrolling(); 580 } 581 } 582 583 private boolean onDragStarted(DragEvent event) { 584 // We handle dropping of items with our email mime type 585 // If the mime type has a mailbox id appended, that is the mailbox of the item 586 // being draged 587 ClipDescription description = event.getClipDescription(); 588 int mimeTypeCount = description.getMimeTypeCount(); 589 for (int i = 0; i < mimeTypeCount; i++) { 590 String mimeType = description.getMimeType(i); 591 if (mimeType.startsWith(EmailProvider.EMAIL_MESSAGE_MIME_TYPE)) { 592 if (DEBUG_DRAG_DROP) { 593 Log.d(TAG, "========== DRAG STARTED"); 594 } 595 mDragItemMailboxId = -1; 596 // See if we find a mailbox id here 597 int dash = mimeType.lastIndexOf('-'); 598 if (dash > 0) { 599 try { 600 mDragItemMailboxId = Long.parseLong(mimeType.substring(dash + 1)); 601 } catch (NumberFormatException e) { 602 // Ignore; we just won't know the mailbox 603 } 604 } 605 mDragInProgress = true; 606 // Stop the list from updating 607 mListAdapter.enableUpdates(false); 608 // Update the backgrounds of our child views to highlight drop targets 609 updateChildViews(); 610 return true; 611 } 612 } 613 return false; 614 } 615 616 private boolean onDrop(DragEvent event) { 617 stopScrolling(); 618 // If we're not on a target, we're done 619 if (mDropTargetAdapterPosition == NO_DROP_TARGET) return false; 620 final Controller controller = Controller.getInstance(mActivity); 621 ClipData clipData = event.getClipData(); 622 int count = clipData.getItemCount(); 623 if (DEBUG_DRAG_DROP) { 624 Log.d(TAG, "Received a drop of " + count + " items."); 625 } 626 // Extract the messageId's to move from the ClipData (set up in MessageListItem) 627 final long[] messageIds = new long[count]; 628 for (int i = 0; i < count; i++) { 629 Uri uri = clipData.getItem(i).getUri(); 630 String msgNum = uri.getPathSegments().get(1); 631 long id = Long.parseLong(msgNum); 632 messageIds[i] = id; 633 } 634 // Call either deleteMessage or moveMessage, depending on the target 635 Utility.runAsync(new Runnable() { 636 @Override 637 public void run() { 638 if (mDropTargetView.mMailboxType == Mailbox.TYPE_TRASH) { 639 for (long messageId: messageIds) { 640 // TODO Get this off UI thread (put in clip) 641 Message msg = Message.restoreMessageWithId(mActivity, messageId); 642 if (msg != null) { 643 controller.deleteMessage(messageId, msg.mAccountKey); 644 } 645 } 646 } else { 647 controller.moveMessage(messageIds, mDropTargetView.mMailboxId); 648 } 649 }}); 650 return true; 651 } 652 653 @Override 654 public boolean onDrag(View view, DragEvent event) { 655 boolean result = false; 656 switch (event.getAction()) { 657 case DragEvent.ACTION_DRAG_STARTED: 658 result = onDragStarted(event); 659 break; 660 case DragEvent.ACTION_DRAG_ENTERED: 661 // The drag has entered the ListView window 662 if (DEBUG_DRAG_DROP) { 663 Log.d(TAG, "========== DRAG ENTERED (target = " + mDropTargetAdapterPosition + 664 ")"); 665 } 666 break; 667 case DragEvent.ACTION_DRAG_EXITED: 668 // The drag has left the building 669 if (DEBUG_DRAG_DROP) { 670 Log.d(TAG, "========== DRAG EXITED (target = " + mDropTargetAdapterPosition + 671 ")"); 672 } 673 onDragExited(); 674 break; 675 case DragEvent.ACTION_DRAG_ENDED: 676 // The drag is over 677 if (DEBUG_DRAG_DROP) { 678 Log.d(TAG, "========== DRAG ENDED"); 679 } 680 onDragEnded(); 681 break; 682 case DragEvent.ACTION_DRAG_LOCATION: 683 // We're moving around within our window; handle scroll, if necessary 684 onDragLocation(event); 685 break; 686 case DragEvent.ACTION_DROP: 687 // The drag item was dropped 688 if (DEBUG_DRAG_DROP) { 689 Log.d(TAG, "========== DROP"); 690 } 691 result = onDrop(event); 692 break; 693 default: 694 break; 695 } 696 return result; 697 } 698} 699