721 lines
27 KiB
Java
721 lines
27 KiB
Java
/*
|
|
* Copyright (C) 2010 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.email.activity;
|
|
|
|
import com.android.email.Controller;
|
|
import com.android.email.Email;
|
|
import com.android.email.R;
|
|
import com.android.email.RefreshManager;
|
|
import com.android.email.provider.EmailProvider;
|
|
import com.android.emailcommon.Logging;
|
|
import com.android.emailcommon.provider.EmailContent.Mailbox;
|
|
import com.android.emailcommon.provider.EmailContent.Message;
|
|
import com.android.emailcommon.utility.Utility;
|
|
|
|
import android.app.Activity;
|
|
import android.app.ListFragment;
|
|
import android.app.LoaderManager;
|
|
import android.app.LoaderManager.LoaderCallbacks;
|
|
import android.content.ClipData;
|
|
import android.content.ClipDescription;
|
|
import android.content.Loader;
|
|
import android.content.res.Resources;
|
|
import android.database.Cursor;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.net.Uri;
|
|
import android.os.Bundle;
|
|
import android.util.Log;
|
|
import android.view.DragEvent;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.View.OnDragListener;
|
|
import android.view.ViewGroup;
|
|
import android.widget.AdapterView;
|
|
import android.widget.ListView;
|
|
import android.widget.AdapterView.OnItemClickListener;
|
|
|
|
import java.security.InvalidParameterException;
|
|
|
|
/**
|
|
* This fragment presents a list of mailboxes for a given account. The "API" includes the
|
|
* following elements which must be provided by the host Activity.
|
|
*
|
|
* - call bindActivityInfo() to provide the account ID and set callbacks
|
|
* - provide callbacks for onOpen and onRefresh
|
|
* - pass-through implementations of onCreateContextMenu() and onContextItemSelected() (temporary)
|
|
*
|
|
* TODO Restoring ListView state -- don't do this when changing accounts
|
|
*/
|
|
public class MailboxListFragment extends ListFragment implements OnItemClickListener,
|
|
OnDragListener {
|
|
private static final String TAG = "MailboxListFragment";
|
|
private static final String BUNDLE_KEY_SELECTED_MAILBOX_ID
|
|
= "MailboxListFragment.state.selected_mailbox_id";
|
|
private static final String BUNDLE_LIST_STATE = "MailboxListFragment.state.listState";
|
|
private static final int LOADER_ID_MAILBOX_LIST = 1;
|
|
private static final boolean DEBUG_DRAG_DROP = false; // MUST NOT SUBMIT SET TO TRUE
|
|
|
|
private static final int NO_DROP_TARGET = -1;
|
|
// Total height of the top and bottom scroll zones, in pixels
|
|
private static final int SCROLL_ZONE_SIZE = 64;
|
|
// The amount of time to scroll by one pixel, in ms
|
|
private static final int SCROLL_SPEED = 4;
|
|
|
|
private RefreshManager mRefreshManager;
|
|
|
|
// UI Support
|
|
private Activity mActivity;
|
|
private MailboxesAdapter mListAdapter;
|
|
private Callback mCallback = EmptyCallback.INSTANCE;
|
|
|
|
private ListView mListView;
|
|
|
|
private boolean mResumed;
|
|
|
|
// Colors used for drop targets
|
|
private static Integer sDropTrashColor;
|
|
private static Drawable sDropActiveDrawable;
|
|
|
|
private long mLastLoadedAccountId = -1;
|
|
private long mAccountId = -1;
|
|
private long mSelectedMailboxId = -1;
|
|
|
|
private boolean mOpenRequested;
|
|
|
|
// True if a drag is currently in progress
|
|
private boolean mDragInProgress = false;
|
|
// The mailbox id of the dragged item's mailbox. We use it to prevent that box from being a
|
|
// valid drop target
|
|
private long mDragItemMailboxId = -1;
|
|
// The adapter position that the user's finger is hovering over
|
|
private int mDropTargetAdapterPosition = NO_DROP_TARGET;
|
|
// The mailbox list item view that the user's finger is hovering over
|
|
private MailboxListItem mDropTargetView;
|
|
// Lazily instantiated height of a mailbox list item (-1 is a sentinel for 'not initialized')
|
|
private int mDragItemHeight = -1;
|
|
// True if we are currently scrolling under the drag item
|
|
private boolean mTargetScrolling;
|
|
|
|
private Utility.ListStateSaver mSavedListState;
|
|
|
|
private MailboxesAdapter.Callback mMailboxesAdapterCallback = new MailboxesAdapter.Callback() {
|
|
@Override
|
|
public void onSetDropTargetBackground(MailboxListItem listItem) {
|
|
listItem.setDropTargetBackground(mDragInProgress, mDragItemMailboxId);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Callback interface that owning activities must implement
|
|
*/
|
|
public interface Callback {
|
|
/** Called when a mailbox (including combined mailbox) is selected. */
|
|
public void onMailboxSelected(long accountId, long mailboxId);
|
|
|
|
/** Called when an account is selected on the combined view. */
|
|
public void onAccountSelected(long accountId);
|
|
|
|
/**
|
|
* Called when the list updates to propagate the current mailbox name and the unread count
|
|
* for it.
|
|
*
|
|
* Note the reason why it's separated from onMailboxSelected is because this needs to be
|
|
* reported when the unread count changes without changing the current mailbox.
|
|
*/
|
|
public void onCurrentMailboxUpdated(long mailboxId, String mailboxName, int unreadCount);
|
|
}
|
|
|
|
private static class EmptyCallback implements Callback {
|
|
public static final Callback INSTANCE = new EmptyCallback();
|
|
@Override public void onMailboxSelected(long accountId, long mailboxId) { }
|
|
@Override public void onAccountSelected(long accountId) { }
|
|
@Override public void onCurrentMailboxUpdated(long mailboxId, String mailboxName,
|
|
int unreadCount) { }
|
|
}
|
|
|
|
/**
|
|
* Called to do initial creation of a fragment. This is called after
|
|
* {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}.
|
|
*/
|
|
@Override
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
|
|
Log.d(Logging.LOG_TAG, "MailboxListFragment onCreate");
|
|
}
|
|
super.onCreate(savedInstanceState);
|
|
|
|
mActivity = getActivity();
|
|
mRefreshManager = RefreshManager.getInstance(mActivity);
|
|
mListAdapter = new MailboxesAdapter(mActivity, MailboxesAdapter.MODE_NORMAL,
|
|
mMailboxesAdapterCallback);
|
|
if (savedInstanceState != null) {
|
|
restoreInstanceState(savedInstanceState);
|
|
}
|
|
if (sDropTrashColor == null) {
|
|
Resources res = getResources();
|
|
sDropTrashColor = res.getColor(R.color.mailbox_drop_destructive_bg_color);
|
|
sDropActiveDrawable = res.getDrawable(R.drawable.list_activated_holo);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public View onCreateView(
|
|
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
return inflater.inflate(R.layout.mailbox_list_fragment, container, false);
|
|
}
|
|
|
|
@Override
|
|
public void onActivityCreated(Bundle savedInstanceState) {
|
|
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
|
|
Log.d(Logging.LOG_TAG, "MailboxListFragment onActivityCreated");
|
|
}
|
|
super.onActivityCreated(savedInstanceState);
|
|
|
|
mListView = getListView();
|
|
mListView.setOnItemClickListener(this);
|
|
mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
|
mListView.setOnDragListener(this);
|
|
registerForContextMenu(mListView);
|
|
}
|
|
|
|
public void setCallback(Callback callback) {
|
|
mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback;
|
|
}
|
|
|
|
private void clearContent() {
|
|
mLastLoadedAccountId = -1;
|
|
mAccountId = -1;
|
|
mSelectedMailboxId = -1;
|
|
|
|
mOpenRequested = false;
|
|
mDragInProgress = false;
|
|
|
|
stopLoader();
|
|
if (mListAdapter != null) {
|
|
mListAdapter.swapCursor(null);
|
|
}
|
|
setListShownNoAnimation(false);
|
|
}
|
|
|
|
/**
|
|
* @param accountId the account we're looking at
|
|
*/
|
|
public void openMailboxes(long accountId) {
|
|
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
|
|
Log.d(Logging.LOG_TAG, "MailboxListFragment openMailboxes");
|
|
}
|
|
if (accountId == -1) {
|
|
throw new InvalidParameterException();
|
|
}
|
|
if (mAccountId == accountId) {
|
|
return;
|
|
}
|
|
clearContent();
|
|
mOpenRequested = true;
|
|
mAccountId = accountId;
|
|
if (mResumed) {
|
|
startLoading();
|
|
}
|
|
}
|
|
|
|
public void setSelectedMailbox(long mailboxId) {
|
|
mSelectedMailboxId = mailboxId;
|
|
if (mResumed) {
|
|
highlightSelectedMailbox(true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when the Fragment is visible to the user.
|
|
*/
|
|
@Override
|
|
public void onStart() {
|
|
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
|
|
Log.d(Logging.LOG_TAG, "MailboxListFragment onStart");
|
|
}
|
|
super.onStart();
|
|
}
|
|
|
|
/**
|
|
* Called when the fragment is visible to the user and actively running.
|
|
*/
|
|
@Override
|
|
public void onResume() {
|
|
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
|
|
Log.d(Logging.LOG_TAG, "MailboxListFragment onResume");
|
|
}
|
|
super.onResume();
|
|
mResumed = true;
|
|
|
|
// If we're recovering from the stopped state, we don't have to reload.
|
|
// (when mOpenRequested = false)
|
|
if (mAccountId != -1 && mOpenRequested) {
|
|
startLoading();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onPause() {
|
|
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
|
|
Log.d(Logging.LOG_TAG, "MailboxListFragment onPause");
|
|
}
|
|
mResumed = false;
|
|
super.onPause();
|
|
mSavedListState = new Utility.ListStateSaver(getListView());
|
|
}
|
|
|
|
/**
|
|
* Called when the Fragment is no longer started.
|
|
*/
|
|
@Override
|
|
public void onStop() {
|
|
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
|
|
Log.d(Logging.LOG_TAG, "MailboxListFragment onStop");
|
|
}
|
|
super.onStop();
|
|
}
|
|
|
|
/**
|
|
* Called when the fragment is no longer in use.
|
|
*/
|
|
@Override
|
|
public void onDestroy() {
|
|
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
|
|
Log.d(Logging.LOG_TAG, "MailboxListFragment onDestroy");
|
|
}
|
|
super.onDestroy();
|
|
}
|
|
|
|
@Override
|
|
public void onSaveInstanceState(Bundle outState) {
|
|
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
|
|
Log.d(Logging.LOG_TAG, "MailboxListFragment onSaveInstanceState");
|
|
}
|
|
super.onSaveInstanceState(outState);
|
|
outState.putLong(BUNDLE_KEY_SELECTED_MAILBOX_ID, mSelectedMailboxId);
|
|
outState.putParcelable(BUNDLE_LIST_STATE, new Utility.ListStateSaver(getListView()));
|
|
}
|
|
|
|
private void restoreInstanceState(Bundle savedInstanceState) {
|
|
mSelectedMailboxId = savedInstanceState.getLong(BUNDLE_KEY_SELECTED_MAILBOX_ID);
|
|
mSavedListState = savedInstanceState.getParcelable(BUNDLE_LIST_STATE);
|
|
}
|
|
|
|
private void startLoading() {
|
|
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
|
|
Log.d(Logging.LOG_TAG, "MailboxListFragment startLoading");
|
|
}
|
|
mOpenRequested = false;
|
|
// Clear the list. (ListFragment will show the "Loading" animation)
|
|
setListShown(false);
|
|
|
|
// If we've already loaded for a different account, discard the previous result and
|
|
// start loading again.
|
|
// We don't want to use restartLoader(), because if the Loader is retained, we *do* want to
|
|
// reuse the previous result.
|
|
// Also, when changing accounts, we don't preserve scroll position.
|
|
boolean accountChanging = false;
|
|
if ((mLastLoadedAccountId != -1) && (mLastLoadedAccountId != mAccountId)) {
|
|
accountChanging = true;
|
|
getLoaderManager().destroyLoader(LOADER_ID_MAILBOX_LIST);
|
|
|
|
// Also, when we're changing account, update the mailbox list if stale.
|
|
refreshMailboxListIfStale();
|
|
}
|
|
getLoaderManager().initLoader(LOADER_ID_MAILBOX_LIST, null,
|
|
new MailboxListLoaderCallbacks(accountChanging));
|
|
}
|
|
|
|
private void stopLoader() {
|
|
final LoaderManager lm = getLoaderManager();
|
|
lm.destroyLoader(LOADER_ID_MAILBOX_LIST);
|
|
}
|
|
|
|
private class MailboxListLoaderCallbacks implements LoaderCallbacks<Cursor> {
|
|
private boolean mAccountChanging;
|
|
|
|
public MailboxListLoaderCallbacks(boolean accountChanging) {
|
|
mAccountChanging = accountChanging;
|
|
}
|
|
|
|
@Override
|
|
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
|
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
|
|
Log.d(Logging.LOG_TAG, "MailboxListFragment onCreateLoader");
|
|
}
|
|
return MailboxesAdapter.createLoader(getActivity(), mAccountId,
|
|
MailboxesAdapter.MODE_NORMAL);
|
|
}
|
|
|
|
@Override
|
|
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
|
if (Email.DEBUG_LIFECYCLE && Email.DEBUG) {
|
|
Log.d(Logging.LOG_TAG, "MailboxListFragment onLoadFinished");
|
|
}
|
|
mLastLoadedAccountId = mAccountId;
|
|
|
|
// Save list view state (primarily scroll position)
|
|
final ListView lv = getListView();
|
|
final Utility.ListStateSaver lss;
|
|
if (mAccountChanging) {
|
|
lss = null; // Don't preserve list state
|
|
} else if (mSavedListState != null) {
|
|
lss = mSavedListState;
|
|
mSavedListState = null;
|
|
} else {
|
|
lss = new Utility.ListStateSaver(lv);
|
|
}
|
|
|
|
if (cursor.getCount() == 0) {
|
|
// If there's no row, don't set it to the ListView.
|
|
// Instead use setListShown(false) to make ListFragment show progress icon.
|
|
mListAdapter.swapCursor(null);
|
|
setListShown(false);
|
|
} else {
|
|
// Set the adapter.
|
|
mListAdapter.swapCursor(cursor);
|
|
setListAdapter(mListAdapter);
|
|
setListShown(true);
|
|
|
|
// We want to make selection visible only when account is changing..
|
|
// i.e. Refresh caused by content changed events shouldn't scroll the list.
|
|
highlightSelectedMailbox(mAccountChanging);
|
|
}
|
|
|
|
// Restore the state
|
|
if (lss != null) {
|
|
lss.restore(lv);
|
|
}
|
|
|
|
// Clear this for next reload triggered by content changed events.
|
|
mAccountChanging = false;
|
|
}
|
|
|
|
@Override
|
|
public void onLoaderReset(Loader<Cursor> loader) {
|
|
mListAdapter.swapCursor(null);
|
|
}
|
|
}
|
|
|
|
public void onItemClick(AdapterView<?> parent, View view, int position,
|
|
long idDontUseIt /* see MailboxesAdapter */ ) {
|
|
final long id = mListAdapter.getId(position);
|
|
if (mListAdapter.isAccountRow(position)) {
|
|
mCallback.onAccountSelected(id);
|
|
} else {
|
|
mCallback.onMailboxSelected(mAccountId, id);
|
|
}
|
|
}
|
|
|
|
public void onRefresh() {
|
|
if (mAccountId != -1) {
|
|
mRefreshManager.refreshMailboxList(mAccountId);
|
|
}
|
|
}
|
|
|
|
private void refreshMailboxListIfStale() {
|
|
if (mRefreshManager.isMailboxListStale(mAccountId)) {
|
|
mRefreshManager.refreshMailboxList(mAccountId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Highlight the selected mailbox.
|
|
*/
|
|
private void highlightSelectedMailbox(boolean ensureSelectionVisible) {
|
|
String mailboxName = "";
|
|
int unreadCount = 0;
|
|
if (mSelectedMailboxId == -1) {
|
|
// No mailbox selected
|
|
mListView.clearChoices();
|
|
} else {
|
|
final int count = mListView.getCount();
|
|
for (int i = 0; i < count; i++) {
|
|
if (mListAdapter.getId(i) != mSelectedMailboxId) {
|
|
continue;
|
|
}
|
|
mListView.setItemChecked(i, true);
|
|
if (ensureSelectionVisible) {
|
|
Utility.listViewSmoothScrollToPosition(getActivity(), mListView, i);
|
|
}
|
|
mailboxName = mListAdapter.getDisplayName(i);
|
|
unreadCount = mListAdapter.getUnreadCount(i);
|
|
break;
|
|
}
|
|
}
|
|
mCallback.onCurrentMailboxUpdated(mSelectedMailboxId, mailboxName, unreadCount);
|
|
}
|
|
|
|
// Drag & Drop handling
|
|
|
|
/**
|
|
* Update all of the list's child views with the proper target background (for now, orange if
|
|
* a valid target, except red if the trash; standard background otherwise)
|
|
*/
|
|
private void updateChildViews() {
|
|
int itemCount = mListView.getChildCount();
|
|
// Lazily initialize the height of our list items
|
|
if (itemCount > 0 && mDragItemHeight < 0) {
|
|
mDragItemHeight = mListView.getChildAt(0).getHeight();
|
|
}
|
|
for (int i = 0; i < itemCount; i++) {
|
|
MailboxListItem item = (MailboxListItem)mListView.getChildAt(i);
|
|
item.setDropTargetBackground(mDragInProgress, mDragItemMailboxId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when our ListView gets a DRAG_EXITED event
|
|
*/
|
|
private void onDragExited() {
|
|
// Reset the background of the current target
|
|
if (mDropTargetAdapterPosition != NO_DROP_TARGET) {
|
|
mDropTargetView.setDropTargetBackground(mDragInProgress, mDragItemMailboxId);
|
|
mDropTargetAdapterPosition = NO_DROP_TARGET;
|
|
}
|
|
stopScrolling();
|
|
}
|
|
|
|
/**
|
|
* Called while dragging; highlight possible drop targets, and autoscroll the list.
|
|
*/
|
|
private void onDragLocation(DragEvent event) {
|
|
// The drag is somewhere in the ListView
|
|
if (mDragItemHeight <= 0) {
|
|
// This shouldn't be possible, but avoid NPE
|
|
return;
|
|
}
|
|
// Find out which item we're in and highlight as appropriate
|
|
int rawTouchY = (int)event.getY();
|
|
int offset = 0;
|
|
if (mListView.getCount() > 0) {
|
|
offset = mListView.getChildAt(0).getTop();
|
|
}
|
|
int targetScreenPosition = (rawTouchY - offset) / mDragItemHeight;
|
|
int firstVisibleItem = mListView.getFirstVisiblePosition();
|
|
int targetAdapterPosition = firstVisibleItem + targetScreenPosition;
|
|
if (targetAdapterPosition != mDropTargetAdapterPosition) {
|
|
if (DEBUG_DRAG_DROP) {
|
|
Log.d(TAG, "========== DROP TARGET " + mDropTargetAdapterPosition + " -> " +
|
|
targetAdapterPosition);
|
|
}
|
|
// Unhighlight the current target, if we've got one
|
|
if (mDropTargetAdapterPosition != NO_DROP_TARGET) {
|
|
mDropTargetView.setDropTargetBackground(true, mDragItemMailboxId);
|
|
}
|
|
// Get the new target mailbox view
|
|
MailboxListItem newTarget =
|
|
(MailboxListItem)mListView.getChildAt(targetScreenPosition);
|
|
// This can be null due to a bug in the framework (checking on that)
|
|
// In any event, we're no longer dragging in the list view if newTarget is null
|
|
if (newTarget == null) {
|
|
if (DEBUG_DRAG_DROP) {
|
|
Log.d(TAG, "========== WTF??? DRAG EXITED");
|
|
}
|
|
onDragExited();
|
|
return;
|
|
} else if (newTarget.mMailboxType == Mailbox.TYPE_TRASH) {
|
|
if (DEBUG_DRAG_DROP) {
|
|
Log.d("onDragLocation", "=== Mailbox " + newTarget.mMailboxId + " TRASH");
|
|
}
|
|
newTarget.setBackgroundColor(sDropTrashColor);
|
|
} else if (newTarget.isDropTarget(mDragItemMailboxId)) {
|
|
if (DEBUG_DRAG_DROP) {
|
|
Log.d("onDragLocation", "=== Mailbox " + newTarget.mMailboxId + " TARGET");
|
|
}
|
|
newTarget.setBackgroundDrawable(sDropActiveDrawable);
|
|
} else {
|
|
if (DEBUG_DRAG_DROP) {
|
|
Log.d("onDragLocation", "=== Mailbox " + newTarget.mMailboxId + " (CALL)");
|
|
}
|
|
targetAdapterPosition = NO_DROP_TARGET;
|
|
newTarget.setDropTargetBackground(true, mDragItemMailboxId);
|
|
}
|
|
// Save away our current position and view
|
|
mDropTargetAdapterPosition = targetAdapterPosition;
|
|
mDropTargetView = newTarget;
|
|
}
|
|
|
|
// This is a quick-and-dirty implementation of drag-under-scroll; something like this
|
|
// should eventually find its way into the framework
|
|
int scrollDiff = rawTouchY - (mListView.getHeight() - SCROLL_ZONE_SIZE);
|
|
boolean scrollDown = (scrollDiff > 0);
|
|
boolean scrollUp = (SCROLL_ZONE_SIZE > rawTouchY);
|
|
if (!mTargetScrolling && scrollDown) {
|
|
int itemsToScroll = mListView.getCount() - targetAdapterPosition;
|
|
int pixelsToScroll = (itemsToScroll + 1) * mDragItemHeight;
|
|
mListView.smoothScrollBy(pixelsToScroll, pixelsToScroll * SCROLL_SPEED);
|
|
if (DEBUG_DRAG_DROP) {
|
|
Log.d(TAG, "========== START TARGET SCROLLING DOWN");
|
|
}
|
|
mTargetScrolling = true;
|
|
} else if (!mTargetScrolling && scrollUp) {
|
|
int pixelsToScroll = (firstVisibleItem + 1) * mDragItemHeight;
|
|
mListView.smoothScrollBy(-pixelsToScroll, pixelsToScroll * SCROLL_SPEED);
|
|
if (DEBUG_DRAG_DROP) {
|
|
Log.d(TAG, "========== START TARGET SCROLLING UP");
|
|
}
|
|
mTargetScrolling = true;
|
|
} else if (!scrollUp && !scrollDown) {
|
|
stopScrolling();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Indicate that scrolling has stopped
|
|
*/
|
|
private void stopScrolling() {
|
|
if (mTargetScrolling) {
|
|
mTargetScrolling = false;
|
|
if (DEBUG_DRAG_DROP) {
|
|
Log.d(TAG, "========== STOP TARGET SCROLLING");
|
|
}
|
|
// Stop the scrolling
|
|
mListView.smoothScrollBy(0, 0);
|
|
}
|
|
}
|
|
|
|
private void onDragEnded() {
|
|
if (mDragInProgress) {
|
|
mDragInProgress = false;
|
|
// Reenable updates to the view and redraw (in case it changed)
|
|
mListAdapter.enableUpdates(true);
|
|
mListAdapter.notifyDataSetChanged();
|
|
// Stop highlighting targets
|
|
updateChildViews();
|
|
// Stop any scrolling that was going on
|
|
stopScrolling();
|
|
}
|
|
}
|
|
|
|
private boolean onDragStarted(DragEvent event) {
|
|
// We handle dropping of items with our email mime type
|
|
// If the mime type has a mailbox id appended, that is the mailbox of the item
|
|
// being draged
|
|
ClipDescription description = event.getClipDescription();
|
|
int mimeTypeCount = description.getMimeTypeCount();
|
|
for (int i = 0; i < mimeTypeCount; i++) {
|
|
String mimeType = description.getMimeType(i);
|
|
if (mimeType.startsWith(EmailProvider.EMAIL_MESSAGE_MIME_TYPE)) {
|
|
if (DEBUG_DRAG_DROP) {
|
|
Log.d(TAG, "========== DRAG STARTED");
|
|
}
|
|
mDragItemMailboxId = -1;
|
|
// See if we find a mailbox id here
|
|
int dash = mimeType.lastIndexOf('-');
|
|
if (dash > 0) {
|
|
try {
|
|
mDragItemMailboxId = Long.parseLong(mimeType.substring(dash + 1));
|
|
} catch (NumberFormatException e) {
|
|
// Ignore; we just won't know the mailbox
|
|
}
|
|
}
|
|
mDragInProgress = true;
|
|
// Stop the list from updating
|
|
mListAdapter.enableUpdates(false);
|
|
// Update the backgrounds of our child views to highlight drop targets
|
|
updateChildViews();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private boolean onDrop(DragEvent event) {
|
|
stopScrolling();
|
|
// If we're not on a target, we're done
|
|
if (mDropTargetAdapterPosition == NO_DROP_TARGET) return false;
|
|
final Controller controller = Controller.getInstance(mActivity);
|
|
ClipData clipData = event.getClipData();
|
|
int count = clipData.getItemCount();
|
|
if (DEBUG_DRAG_DROP) {
|
|
Log.d(TAG, "Received a drop of " + count + " items.");
|
|
}
|
|
// Extract the messageId's to move from the ClipData (set up in MessageListItem)
|
|
final long[] messageIds = new long[count];
|
|
for (int i = 0; i < count; i++) {
|
|
Uri uri = clipData.getItemAt(i).getUri();
|
|
String msgNum = uri.getPathSegments().get(1);
|
|
long id = Long.parseLong(msgNum);
|
|
messageIds[i] = id;
|
|
}
|
|
// Call either deleteMessage or moveMessage, depending on the target
|
|
Utility.runAsync(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (mDropTargetView.mMailboxType == Mailbox.TYPE_TRASH) {
|
|
for (long messageId: messageIds) {
|
|
// TODO Get this off UI thread (put in clip)
|
|
Message msg = Message.restoreMessageWithId(mActivity, messageId);
|
|
if (msg != null) {
|
|
controller.deleteMessage(messageId, msg.mAccountKey);
|
|
}
|
|
}
|
|
} else {
|
|
controller.moveMessage(messageIds, mDropTargetView.mMailboxId);
|
|
}
|
|
}});
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean onDrag(View view, DragEvent event) {
|
|
boolean result = false;
|
|
switch (event.getAction()) {
|
|
case DragEvent.ACTION_DRAG_STARTED:
|
|
result = onDragStarted(event);
|
|
break;
|
|
case DragEvent.ACTION_DRAG_ENTERED:
|
|
// The drag has entered the ListView window
|
|
if (DEBUG_DRAG_DROP) {
|
|
Log.d(TAG, "========== DRAG ENTERED (target = " + mDropTargetAdapterPosition +
|
|
")");
|
|
}
|
|
break;
|
|
case DragEvent.ACTION_DRAG_EXITED:
|
|
// The drag has left the building
|
|
if (DEBUG_DRAG_DROP) {
|
|
Log.d(TAG, "========== DRAG EXITED (target = " + mDropTargetAdapterPosition +
|
|
")");
|
|
}
|
|
onDragExited();
|
|
break;
|
|
case DragEvent.ACTION_DRAG_ENDED:
|
|
// The drag is over
|
|
if (DEBUG_DRAG_DROP) {
|
|
Log.d(TAG, "========== DRAG ENDED");
|
|
}
|
|
onDragEnded();
|
|
break;
|
|
case DragEvent.ACTION_DRAG_LOCATION:
|
|
// We're moving around within our window; handle scroll, if necessary
|
|
onDragLocation(event);
|
|
break;
|
|
case DragEvent.ACTION_DROP:
|
|
// The drag item was dropped
|
|
if (DEBUG_DRAG_DROP) {
|
|
Log.d(TAG, "========== DROP");
|
|
}
|
|
result = onDrop(event);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
}
|