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