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