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