diff --git a/res/layout/draggable_row.xml b/res/layout/draggable_row.xml new file mode 100644 index 00000000..24f1bd88 --- /dev/null +++ b/res/layout/draggable_row.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + diff --git a/res/layout/playlist_row.xml b/res/layout/playlist_row.xml deleted file mode 100644 index b6d3751a..00000000 --- a/res/layout/playlist_row.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - diff --git a/res/layout/showqueue_listview.xml b/res/layout/showqueue_listview.xml index 7888fa07..a26d4cac 100644 --- a/res/layout/showqueue_listview.xml +++ b/res/layout/showqueue_listview.xml @@ -5,13 +5,12 @@ android:id="@+id/list" android:layout_width="fill_parent" android:layout_height="fill_parent" - android:divider="@color/divider_color" - android:dividerHeight="1dip" android:listSelector="@drawable/selectable_item_bg" android:scrollbarStyle="outsideInset" + android:divider="@null" dslv:drag_enabled="true" dslv:drag_start_mode="onMove" - dslv:drag_handle_id="@+id/playmark" + dslv:drag_handle_id="@+id/dragger" dslv:remove_enabled="true" dslv:remove_mode="flingRemove" /> diff --git a/res/layout/showqueue_row.xml b/res/layout/showqueue_row.xml deleted file mode 100644 index 52908a70..00000000 --- a/res/layout/showqueue_row.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - diff --git a/res/layout/tab_order_row.xml b/res/layout/tab_order_row.xml deleted file mode 100644 index e9bc2c2c..00000000 --- a/res/layout/tab_order_row.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - diff --git a/src/ch/blinkenlights/android/vanilla/DragListView.java b/src/ch/blinkenlights/android/vanilla/DragListView.java deleted file mode 100644 index 9aab08a7..00000000 --- a/src/ch/blinkenlights/android/vanilla/DragListView.java +++ /dev/null @@ -1,443 +0,0 @@ -/* - * Copyright (C) 2008 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 ch.blinkenlights.android.vanilla; - -import android.content.Context; -import android.graphics.Bitmap; -import android.os.Handler; -import android.os.Message; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.AdapterView; -import android.widget.ImageView; -import android.widget.ListAdapter; -import android.widget.ListView; - -/** - * A ListView that supports dragging to reorder its elements. - * - * This implementation has some restrictions: - * Footers are unsupported - * All non-header views must have the same height - * The adapter must implement DragAdapter - * - * Dragging disabled by default. Enable it with - * {@link DragListView#setEditable(boolean)}. - * - * This should really be built-in to Android. This implementation is SUPER- - * HACKY. : / - */ -public class DragListView extends ListView implements Handler.Callback { - /** - * Adapter that implements move and remove operations. - */ - public interface DragAdapter extends ListAdapter { - /** - * Remove the element at position from and insert it at position to. - */ - public void move(int from, int to); - /** - * Remove the element at the given position. - */ - public void remove(int position); - } - - /** - * Sent to scroll the list up or down when the dragged view is near the - * top or bottom of the list. - */ - private static final int MSG_SCROLL = 0; - /** - * Height of each row in dip. - */ - public static final int ROW_HEIGHT = 44; - /** - * Padding for each row in dip. - */ - public static final int PADDING = 0; - /** - * Background color of row while it is being dragged. - */ - public static final int DRAG_COLOR = 0xff005500; - /** - * A handler running on the UI thread. - */ - private final Handler mHandler = new Handler(this); - /** - * The system window manager instance. - */ - private WindowManager mWindowManager; - /** - * The adapter that will be called to move/remove rows. - */ - private DragAdapter mAdapter; - /** - * True to allow dragging; false otherwise. - */ - private boolean mEditable; - /** - * Scaled height of each row in pixels. - */ - private final int mRowHeight; - /** - * The view that is actually dragged around during a drag. (The original - * view is hidden). - */ - private ImageView mDragView; - /** - * A copy of the dragged row's scrolling cache that is shown in mDragView. - */ - private Bitmap mDragBitmap; - /** - * Window params for the drag view window. Used to move the window around. - */ - private WindowManager.LayoutParams mWindowParams; - /** - * At which position is the item currently being dragged. Note that this - * takes in to account header items. - */ - private int mDragPos; - /** - * At which position was the item being dragged originally - */ - private int mSrcDragPos; - /** - * At what y offset inside the dragged view did the user grab it. - */ - private int mDragPointY; - /** - * The difference between screen coordinates and coordinates in the drag - * view. - */ - private int mYOffset; - /** - * The y coordinate of the top of the drag view after the last motion - * event. - */ - private int mLastMotionY; - /** - * Default padding for rows. - */ - private final int mPadding; - - public DragListView(Context context, AttributeSet attrs) - { - super(context, attrs); - - float density = context.getResources().getDisplayMetrics().density; - mPadding = (int)(PADDING * density); - mRowHeight = (int)(ROW_HEIGHT * density); - } - - /** - * This should be called instead of - * {@link ListView#setAdapter(android.widget.ListAdapter)}. - * DragListView requires a DragAdapter to handle move/remove callbacks - * from dragging. - * - * @param adapter The adapter to use. Will be passed to - * {@link ListView#setAdapter(android.widget.ListAdapter)}. - */ - public void setAdapter(DragAdapter adapter) - { - super.setAdapter(adapter); - // Keep track of adapter here since getAdapter() will return a wrapper - // when there are headers. - mAdapter = adapter; - } - - /** - * Set whether to allow elements to be reordered. - * - * @param editable True to allow reordering. - */ - public void setEditable(boolean editable) - { - mEditable = editable; - if (!editable) - stopDragging(); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) - { - if (mEditable) { - switch (ev.getAction()) { - case MotionEvent.ACTION_DOWN: - stopDragging(); - - int x = (int)ev.getX(); - // The left quarter of the item is the grabber for dragging the item - if (x < getWidth() / 4) { - int item = pointToPosition(x, (int)ev.getY()); - if (item != AdapterView.INVALID_POSITION && item >= getHeaderViewsCount()) { - startDragging(item, ev); - return false; - } - } - break; - } - } - - return super.onInterceptTouchEvent(ev); - } - - @Override - public boolean onTouchEvent(MotionEvent ev) - { - if (!mEditable || mDragView == null) - return super.onTouchEvent(ev); - - switch (ev.getAction()) { - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - stopDragging(); - int offset = getHeaderViewsCount(); - if (mDragPos >= offset && mDragPos < getCount()) - mAdapter.move(mSrcDragPos - offset, mDragPos - offset); - break; - case MotionEvent.ACTION_MOVE: - int y = (int)ev.getY() - mDragPointY; - mLastMotionY = y; - mWindowParams.x = 0; - mWindowParams.y = y + mYOffset; - mWindowManager.updateViewLayout(mDragView, mWindowParams); - computeDragPosition(y); - break; - } - - return true; - } - - /** - * Restore size and visibility for all list items - */ - private void unExpandViews() - { - int padding = mPadding; - for (int i = 0, count = getChildCount(); i != count; ++i) { - View view = getChildAt(i); - ViewGroup.LayoutParams params = view.getLayoutParams(); - params.height = 0; - view.setLayoutParams(params); - view.setVisibility(View.VISIBLE); - view.setPadding(padding, padding, padding, padding); - } - } - - /** - * Adjust visibility and size to make it appear as though - * an item is being dragged around and other items are making - * room for it. - * - * If dropping the item would result in it still being in the - * same place, then make the dragged list item's size normal, - * but make the item invisible. - * Otherwise, if the dragged list item is still on screen, make - * it as small as possible and expand the item below the insert - * point. - */ - private void doExpansion() - { - int firstVisibile = getFirstVisiblePosition(); - int childNum = mDragPos - firstVisibile; - if (mDragPos > mSrcDragPos) - childNum += 1; - - int headerCount = getHeaderViewsCount(); - int childCount = getChildCount(); - - View dragSrcView = getChildAt(mSrcDragPos - firstVisibile); - - int start = firstVisibile < headerCount ? headerCount - firstVisibile : 0; - int padding = mPadding; - int rowHeight = mRowHeight; - int nextHeight = rowHeight; - - for (int i = start; i != childCount; ++i) { - View view = getChildAt(i); - - int height = nextHeight; - nextHeight = rowHeight; - int visibility = View.VISIBLE; - int paddingBottom = padding; - int paddingTop = padding; - - if (view == dragSrcView) { - if (mDragPos == mSrcDragPos) { - // hovering over the original location: show empty space - visibility = View.INVISIBLE; - height += 1; - } else { - // not hovering over it: show nothing - // Ideally the item would be completely gone, but neither - // setting its size to 0 nor settings visibility to GONE - // has the desired effect. - height = 1; - } - nextHeight -= 1; - } else if (i == childNum) { - // hovering over this row; expand it to "make room" for the - // dragged item - paddingTop += height; - height *= 2; - } else if (childNum == childCount && i == childCount - 1) { - // hovering over the bottom of the list: we need to "make room" - // at the bottom - paddingBottom += height; - height *= 2; - } - - view.setPadding(padding, paddingTop, padding, paddingBottom); - view.setVisibility(visibility); - ViewGroup.LayoutParams params = view.getLayoutParams(); - params.height = height; - view.setLayoutParams(params); - } - } - - /** - * Computes the drag position based on where the drag view is hovering. - * Expands views and updates scrolling when this position changes. - * - * @param y The y coordinate of the top of the drag view. - * @return The scrolling speed in pixels - */ - private int computeDragPosition(int y) - { - // This assumes uniform height for all non-header rows - int firstVisible = getFirstVisiblePosition(); - int topPos = Math.max(getHeaderViewsCount(), firstVisible); - int dragHeight = mRowHeight; - View view = getChildAt(topPos - firstVisible); - int viewMiddle = view.getTop() + dragHeight / 2; - int dragPos = Math.min(getCount() - 1, topPos + Math.max(0, y - viewMiddle + dragHeight) / dragHeight); - - if (dragPos != mDragPos) { - mDragPos = dragPos; - doExpansion(); - } - - int height = getHeight(); - int upperBound = height / 4; - int lowerBound = height * 3 / 4; - - if (y > lowerBound && (getLastVisiblePosition() < getCount() - 1 || getChildAt(getChildCount() - 1).getBottom() > getBottom())) - return y > (height + lowerBound) / 2 ? 16 : 4; - else if (y < upperBound && (getFirstVisiblePosition() != 0 || getChildAt(0).getTop() < 0)) - return y < upperBound / 2 ? -16 : -4; - - return 0; - } - - /** - * Start a drag operation. - * - * @param row The row number of the item to drag - * @param ev The touch event that started this drag. - */ - private void startDragging(int row, MotionEvent ev) - { - int y = (int)ev.getY(); - - View item = getChildAt(row - getFirstVisiblePosition()); - mDragPointY = y - item.getTop(); - mYOffset = (int)ev.getRawY() - y; - - mWindowParams = new WindowManager.LayoutParams(); - mWindowParams.gravity = Gravity.TOP | Gravity.LEFT; - mWindowParams.x = 0; - mWindowParams.y = y - mDragPointY + mYOffset; - - mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT; - mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT; - mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE - | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; - mWindowParams.windowAnimations = 0; - - int color = item.getDrawingCacheBackgroundColor(); - item.setDrawingCacheBackgroundColor(0xff005500); - item.buildDrawingCache(); - // Create a copy of the drawing cache so that it does not get recycled - // by the framework when the list tries to clean up memory - Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache()); - item.setDrawingCacheBackgroundColor(color); - item.destroyDrawingCache(); - mDragBitmap = bitmap; - - Context context = getContext(); - ImageView view = new ImageView(context); - view.setPadding(0, 0, 0, 0); - view.setImageBitmap(bitmap); - - mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); - mWindowManager.addView(view, mWindowParams); - mDragView = view; - - mSrcDragPos = row; - // Force expansion on next motion event - mDragPos = INVALID_POSITION; - - mHandler.sendEmptyMessageDelayed(MSG_SCROLL, 50); - } - - /** - * Stop a drag operation. - */ - private void stopDragging() - { - if (mDragView != null) { - mDragView.setVisibility(GONE); - mWindowManager.removeView(mDragView); - mDragView.setImageDrawable(null); - mDragView = null; - } - if (mDragBitmap != null) { - mDragBitmap.recycle(); - mDragBitmap = null; - } - unExpandViews(); - mHandler.removeMessages(MSG_SCROLL); - } - - @Override - public boolean handleMessage(Message message) - { - if (message.what == MSG_SCROLL) { - if (mDragPos != INVALID_POSITION) { - int speed = computeDragPosition(mLastMotionY); - if (speed != 0) { - View view = getChildAt(0); - if (view != null) { - int pos = view.getTop(); - setSelectionFromTop(getFirstVisiblePosition(), pos - speed); - } - } - } - mHandler.sendEmptyMessageDelayed(MSG_SCROLL, 50); - } - - return true; - } -} diff --git a/src/ch/blinkenlights/android/vanilla/DragTextView.java b/src/ch/blinkenlights/android/vanilla/DragTextView.java deleted file mode 100644 index db71203a..00000000 --- a/src/ch/blinkenlights/android/vanilla/DragTextView.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2012 Christopher Eby - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package ch.blinkenlights.android.vanilla; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.widget.Checkable; -import android.widget.TextView; - -/** - * TextView that draws a divider line at the bottom. - * - * We draw the divider here rather than with ListView.setDivider() so we don't - * have duplicate dividers when hiding a row for a drag. - * - * This also implements the Checkable interface to provide checking for - * TabOrderActivity. CheckedTextView also provides this, but unfortunately its - * check-mark ignores padding so it can't be used with DragListView's expansion - * code. - */ -public class DragTextView extends TextView implements Checkable { - private final Paint mPaint; - private boolean mChecked; - /** - * Check mark drawable to update with checked state. This drawable is set - * as the TextView's right compound drawable, so TextView will handle the - * drawing. - */ - private final Drawable mCheckMarkDrawable; - /** - * The preferred height of the view in pixels. Set to DragListView.ROW_HEIGHT. - */ - private final int mHeight; - - private static final int[] CHECKED_STATE_SET = { - android.R.attr.state_checked - }; - - public DragTextView(Context context, AttributeSet attrs) - { - super(context, attrs); - - Paint paint = new Paint(); - paint.setColor(0xff444444); - mPaint = paint; - Drawable[] drawables = getCompoundDrawables(); - mCheckMarkDrawable = drawables[2]; - - float density = context.getResources().getDisplayMetrics().density; - mHeight = (int)(DragListView.ROW_HEIGHT * density); - } - - @Override - public void onDraw(Canvas canvas) - { - int height = getHeight(); - if (height <= 1) - return; - super.onDraw(canvas); - if (getDrawingCacheBackgroundColor() != DragListView.DRAG_COLOR && getPaddingBottom() < getHeight() / 2) { - // only draw divider when not dragging - float h = height - 1; - canvas.drawLine(0, h, getWidth(), h, mPaint); - } - } - - @Override - public boolean isChecked() - { - return mChecked; - } - - @Override - public void setChecked(boolean checked) - { - mChecked = checked; - refreshDrawableState(); - } - - @Override - public void toggle() - { - setChecked(!mChecked); - } - - @Override - protected int[] onCreateDrawableState(int extraSpace) - { - final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); - if (mChecked) { - mergeDrawableStates(drawableState, CHECKED_STATE_SET); - } - return drawableState; - } - - @Override - protected void drawableStateChanged() - { - super.drawableStateChanged(); - - if (mCheckMarkDrawable != null) { - int[] myDrawableState = getDrawableState(); - mCheckMarkDrawable.setState(myDrawableState); - invalidate(); - } - } - - @Override - public void onMeasure(int widthSpec, int heightSpec) - { - setMeasuredDimension(MeasureSpec.getSize(widthSpec), resolveSize(mHeight, heightSpec)); - } -} diff --git a/src/ch/blinkenlights/android/vanilla/PlaylistAdapter.java b/src/ch/blinkenlights/android/vanilla/PlaylistAdapter.java index 246a7236..81f81bed 100644 --- a/src/ch/blinkenlights/android/vanilla/PlaylistAdapter.java +++ b/src/ch/blinkenlights/android/vanilla/PlaylistAdapter.java @@ -118,7 +118,7 @@ public class PlaylistAdapter extends CursorAdapter implements Handler.Callback { @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(R.layout.playlist_row, null); + return mInflater.inflate(R.layout.draggable_row, null); } /** diff --git a/src/ch/blinkenlights/android/vanilla/ShowQueueActivity.java b/src/ch/blinkenlights/android/vanilla/ShowQueueActivity.java index a7f9ef09..9c84c8b1 100644 --- a/src/ch/blinkenlights/android/vanilla/ShowQueueActivity.java +++ b/src/ch/blinkenlights/android/vanilla/ShowQueueActivity.java @@ -43,7 +43,7 @@ public class ShowQueueActivity extends Activity { mService = PlaybackService.get(this); mListView = (DragSortListView) findViewById(R.id.list); - listAdapter = new ShowQueueAdapter(this, R.layout.showqueue_row); + listAdapter = new ShowQueueAdapter(this, R.layout.draggable_row); mListView.setAdapter(listAdapter); mListView.setFastScrollAlwaysVisible(true); mListView.setDropListener(onDrop); diff --git a/src/ch/blinkenlights/android/vanilla/ShowQueueAdapter.java b/src/ch/blinkenlights/android/vanilla/ShowQueueAdapter.java index a9ece20e..d3e8660d 100644 --- a/src/ch/blinkenlights/android/vanilla/ShowQueueAdapter.java +++ b/src/ch/blinkenlights/android/vanilla/ShowQueueAdapter.java @@ -71,8 +71,8 @@ public class ShowQueueAdapter target.setText(sb); } - View pmark = ((View)row.findViewById(R.id.playmark)); - pmark.setVisibility( ( position == mHighlightRow ? View.VISIBLE : View.INVISIBLE )); + View dragger = ((View)row.findViewById(R.id.dragger)); + dragger.setVisibility( ( position == mHighlightRow ? View.VISIBLE : View.INVISIBLE )); return row; } diff --git a/src/ch/blinkenlights/android/vanilla/TabOrderAdapter.java b/src/ch/blinkenlights/android/vanilla/TabOrderAdapter.java index 28dea3d1..6f8f15f1 100644 --- a/src/ch/blinkenlights/android/vanilla/TabOrderAdapter.java +++ b/src/ch/blinkenlights/android/vanilla/TabOrderAdapter.java @@ -26,6 +26,7 @@ import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.TextView; import android.widget.BaseAdapter; /** @@ -86,14 +87,14 @@ public class TabOrderAdapter extends BaseAdapter { @Override public View getView(int position, View convert, ViewGroup parent) { - DragTextView text; + View view; if (convert == null) { - text = (DragTextView)mInflater.inflate(R.layout.tab_order_row, null); + view = mInflater.inflate(R.layout.draggable_row, null); } else { - text = (DragTextView)convert; + view = convert; } - text.setText(LibraryPagerAdapter.TITLES[mTabIds[position]]); - return text; + ((TextView)view.findViewById(R.id.text)).setText(LibraryPagerAdapter.TITLES[mTabIds[position]]); + return view; } @Override