Implement proper drag-n-drop

Still some tweaks to be done, but, we're real close to what it should
look like.

Bug 3322756
Change-Id: I8549ab59677a344e8f259c88bed950169d7381a8
This commit is contained in:
Todd Kennedy 2011-01-09 11:13:29 -08:00
parent 285fac8554
commit 07f5f60b8f
6 changed files with 128 additions and 42 deletions

View File

@ -44,6 +44,13 @@
<color name="combined_view_account_color_4">#fff4fd04</color>
<color name="combined_view_account_color_5">#ffe65020</color>
<!-- Drop target colors -->
<!-- STOPSHIP Replace with final colors -->
<color name="mailbox_drop_available_color">#ffd9e3bd</color>
<color name="mailbox_drop_unavailable_color">#ffffffff</color>
<color name="mailbox_drop_target_active_color">#ffbedc62</color>
<color name="mailbox_drop_target_destructive_color">#ffff0000</color>
<!-- Widget colors -->
<color name="widget_label_shadow_color">#0d0d0d</color>
<color name="widget_account_color">#666666</color>

View File

@ -44,6 +44,12 @@
<dimen name="widget_subject_font_size">14sp</dimen>
<dimen name="widget_date_font_size">14sp</dimen>
<dimen name="message_list_drag_offset">16dip</dimen>
<dimen name="message_list_drag_count_font_size">42sp</dimen>
<dimen name="message_list_drag_count_left_margin">16dip</dimen>
<dimen name="message_list_drag_message_font_size">18sp</dimen>
<dimen name="message_list_drag_message_right_margin">16dip</dimen>
<dimen name="message_compose_header_button_area_width">120dip</dimen>
</resources>

View File

@ -129,10 +129,8 @@
may be too large to view.</string>
<!-- Text shown with dragged messages to indicate how many are being dragged [CHAR LIMIT=40]-->
<plurals name="move_messages">
<item quantity="one">Move <xliff:g id="num_message" example="1">%1$d</xliff:g> message
</item>
<item quantity="other">Move <xliff:g id="num_message" example="3">%1$d</xliff:g> messages
</item>
<item quantity="one">Move message</item>
<item quantity="other">Move messages</item>
</plurals>
<!-- Toast shown when a message(s) can't be moved because it's not supported by the POP3
protocol. [CHAR LIMIT=none]-->

View File

@ -31,6 +31,7 @@ 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.net.Uri;
import android.os.Bundle;
@ -69,6 +70,10 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList
// Top and bottom scroll zone size, in pixels
private static final int SCROLL_ZONE_SIZE = 64;
// Colors used for drop targets
private static Integer sDropTrashColor;
private static Integer sDropActiveColor;
private long mLastLoadedAccountId = -1;
private long mAccountId = -1;
private long mSelectedMailboxId = -1;
@ -85,11 +90,6 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList
private boolean mOpenRequested;
private boolean mResumed;
// Color used when we're dragging over a valid drop target
private static final int DROP_TARGET = 0xFFFFCC33;
// Color used when we're dragging over a "delete" target (i.e. the trash mailbox)
private static final int DROP_TARGET_TRASH = 0xFFFF0000;
// 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
@ -161,6 +161,11 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList
if (savedInstanceState != null) {
restoreInstanceState(savedInstanceState);
}
if (sDropTrashColor == null) {
Resources res = getResources();
sDropTrashColor = res.getColor(R.color.mailbox_drop_target_destructive_color);
sDropActiveColor = res.getColor(R.color.mailbox_drop_target_active_color);
}
}
@Override
@ -500,10 +505,10 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList
return;
} else if (newTarget.mMailboxType == Mailbox.TYPE_TRASH) {
Log.d("onDragLocation", "=== Mailbox " + newTarget.mMailboxId + " TRASH");
newTarget.setBackgroundColor(DROP_TARGET_TRASH);
newTarget.setBackgroundColor(sDropTrashColor);
} else if (newTarget.isDropTarget(mDragItemMailboxId)) {
Log.d("onDragLocation", "=== Mailbox " + newTarget.mMailboxId + " TARGET");
newTarget.setBackgroundColor(DROP_TARGET);
newTarget.setBackgroundColor(sDropActiveColor);
} else {
Log.d("onDragLocation", "=== Mailbox " + newTarget.mMailboxId + " (CALL)");
targetAdapterPosition = NO_DROP_TARGET;

View File

@ -16,20 +16,21 @@
package com.android.email.activity;
import com.android.email.R;
import com.android.email.provider.EmailContent.Mailbox;
import com.android.internal.util.ArrayUtils;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.RelativeLayout;
public class MailboxListItem extends RelativeLayout {
// STOPSHIP Need final color/ui
// Color used for valid drop targets
private static final int DROP_AVAILABLE = 0xFFFFFF33;
private static final int DROP_UNAVAILABLE = 0xFFFFFFFF;
// Colors used for drop targets
private static Integer sDropAvailableColor;
private static Integer sDropUnavailableColor;
public long mMailboxId;
public Integer mMailboxType;
@ -53,6 +54,11 @@ public class MailboxListItem extends RelativeLayout {
protected void onFinishInflate() {
super.onFinishInflate();
mBackground = getBackground();
if (sDropAvailableColor == null) {
Resources res = getResources();
sDropAvailableColor = res.getColor(R.color.mailbox_drop_available_color);
sDropUnavailableColor = res.getColor(R.color.mailbox_drop_unavailable_color);
}
}
public boolean isDropTarget(long itemMailbox) {
@ -65,10 +71,10 @@ public class MailboxListItem extends RelativeLayout {
public boolean setDropTargetBackground(boolean dragInProgress, long itemMailbox) {
if (dragInProgress) {
if (isDropTarget(itemMailbox)) {
setBackgroundColor(DROP_AVAILABLE);
setBackgroundColor(sDropAvailableColor);
return true;
} else {
setBackgroundColor(DROP_UNAVAILABLE);
setBackgroundColor(sDropUnavailableColor);
return false;
}
} else {

View File

@ -37,10 +37,14 @@ import android.content.ClipData;
import android.content.ContentUris;
import android.content.Context;
import android.content.Loader;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
@ -55,9 +59,11 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnDragListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
@ -88,7 +94,7 @@ import java.util.Set;
*/
public class MessageListFragment extends ListFragment
implements OnItemClickListener, OnItemLongClickListener, MessagesAdapter.Callback,
MoveMessageToDialog.Callback, OnDragListener {
MoveMessageToDialog.Callback, OnDragListener, OnTouchListener {
private static final String BUNDLE_LIST_STATE = "MessageListFragment.state.listState";
private static final String BUNDLE_KEY_SELECTED_MESSAGE_ID
= "messageListFragment.state.listState.selected_message_id";
@ -240,6 +246,7 @@ public class MessageListFragment extends ListFragment
mListView = getListView();
mListView.setOnItemClickListener(this);
mListView.setOnItemLongClickListener(this);
mListView.setOnTouchListener(this);
mListView.setItemsCanFocus(false);
mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
@ -457,47 +464,94 @@ public class MessageListFragment extends ListFragment
}
// This is tentative drag & drop UI
// STOPSHIP this entire class needs to be rewritten based on the actual UI design
private static class ShadowBuilder extends DragShadowBuilder {
private static Drawable sBackground;
private static TextPaint sPaint;
/** Paint information for the move message text */
private static TextPaint sMessagePaint;
/** Paint information for the message count */
private static TextPaint sCountPaint;
/** The x location of any touch event; used to ensure the drag overlay is drawn correctly */
private static int sTouchX;
// TODO Get actual dimension from UI
private static final int mWidth = 250;
private final int mHeight;
private String mDragDesc;
private float mDragDescX;
private float mDragDescY;
/** Width of the draggable view */
private final int mDragWidth;
/** Height of the draggable view */
private final int mDragHeight;
private String mMessageText;
private PointF mMessagePoint;
private String mCountText;
private PointF mCountPoint;
private int mOldOrientation = Configuration.ORIENTATION_UNDEFINED;
/** Margin applied to the right of count text */
private static float sCountMargin;
/** Margin applied to left of the message text */
private static float sMessageMargin;
/** Vertical offset of the drag view */
private static int sDragOffset;
public ShadowBuilder(View view, int count) {
super(view);
Resources resources = view.getResources();
// TODO Get actual dimension from UI
mHeight = view.getHeight();
mDragDesc = resources.getQuantityString(R.plurals.move_messages, count, count);
mDragDescX = 60;
// Use height of this font??
mDragDescY = view.getHeight() / 2;
if (sBackground == null) {
sBackground = resources.getDrawable(R.drawable.drag_background_holo);
sBackground.setBounds(0, 0, mWidth, view.getHeight());
sPaint = new TextPaint();
sPaint.setTypeface(Typeface.DEFAULT_BOLD);
sPaint.setTextSize(18);
Resources res = view.getResources();
int newOrientation = res.getConfiguration().orientation;
mDragHeight = view.getHeight();
mDragWidth = view.getWidth();
// TODO: Can we define a layout for the contents of the drag area?
if (sBackground == null || mOldOrientation != newOrientation) {
mOldOrientation = newOrientation;
sBackground = res.getDrawable(R.drawable.drag_background_holo);
sBackground.setBounds(0, 0, mDragWidth, mDragHeight);
sDragOffset = (int)res.getDimension(R.dimen.message_list_drag_offset);
sMessagePaint = new TextPaint();
float messageTextSize;
messageTextSize = res.getDimension(R.dimen.message_list_drag_message_font_size);
sMessagePaint.setTextSize(messageTextSize);
sMessagePaint.setTypeface(Typeface.DEFAULT_BOLD);
sMessagePaint.setAntiAlias(true);
sMessageMargin = res.getDimension(R.dimen.message_list_drag_message_right_margin);
sCountPaint = new TextPaint();
float countTextSize;
countTextSize = res.getDimension(R.dimen.message_list_drag_count_font_size);
sCountPaint.setTextSize(countTextSize);
sCountPaint.setTypeface(Typeface.DEFAULT_BOLD);
sCountPaint.setAntiAlias(true);
sCountMargin = res.getDimension(R.dimen.message_list_drag_count_left_margin);
}
// Calculate layout positions
Rect b = new Rect();
mMessageText = res.getQuantityString(R.plurals.move_messages, count, count);
sMessagePaint.getTextBounds(mMessageText, 0, mMessageText.length(), b);
mMessagePoint = new PointF(mDragWidth - b.right - sMessageMargin,
(mDragHeight - b.top)/ 2);
mCountText = Integer.toString(count);
sCountPaint.getTextBounds(mCountText, 0, mCountText.length(), b);
mCountPoint = new PointF(sCountMargin,
(mDragHeight - b.top) / 2);
}
@Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
shadowSize.set(mWidth, mHeight);
shadowTouchPoint.set(20, mHeight / 2);
shadowSize.set(mDragWidth, mDragHeight);
shadowTouchPoint.set(sTouchX, (mDragHeight / 2) + sDragOffset);
}
@Override
public void onDrawShadow(Canvas canvas) {
super.onDrawShadow(canvas);
sBackground.draw(canvas);
canvas.drawText(mDragDesc, mDragDescX, mDragDescY, sPaint);
canvas.drawText(mMessageText, mMessagePoint.x, mMessagePoint.y, sMessagePaint);
canvas.drawText(mCountText, mCountPoint.x, mCountPoint.y, sCountPaint);
}
}
@ -512,6 +566,16 @@ public class MessageListFragment extends ListFragment
return false;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// Save the touch location to draw the drag overlay at the correct location
ShadowBuilder.sTouchX = (int)event.getX();
}
// don't do anything, let the system process the event
return false;
}
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (view != mListFooterView) {
// We can't move from combined accounts view