mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-27 20:36:51 +01:00
Experiment with a draggable sort list.
I left the non-preference related dslv code untouched and took it from here https://github.com/sbolotovms/drag-sort-listview (This is one of many forks, which had migrated android to androidx) The base for the code in DragSortListPreferenceFragment.java and DragSortListPreference.java comes from: https://github.com/kd7uiy/drag-sort-listview I heavily modiefied it moved it to androidx
This commit is contained in:
parent
c734333ad4
commit
e1f2e0c830
@ -0,0 +1,38 @@
|
|||||||
|
package com.mobeta.android.dslv;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.Checkable;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
public class CheckableLinearLayout extends LinearLayout implements Checkable {
|
||||||
|
|
||||||
|
private static final int CHECKABLE_CHILD_INDEX = 1;
|
||||||
|
private Checkable child;
|
||||||
|
|
||||||
|
public CheckableLinearLayout(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinishInflate() {
|
||||||
|
super.onFinishInflate();
|
||||||
|
child = (Checkable) getChildAt(CHECKABLE_CHILD_INDEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChecked() {
|
||||||
|
return child.isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setChecked(boolean checked) {
|
||||||
|
child.setChecked(checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toggle() {
|
||||||
|
child.toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,474 @@
|
|||||||
|
package com.mobeta.android.dslv;
|
||||||
|
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.view.GestureDetector;
|
||||||
|
import android.view.HapticFeedbackConstants;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewConfiguration;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that starts and stops item drags on a {@link DragSortListView}
|
||||||
|
* based on touch gestures. This class also inherits from
|
||||||
|
* {@link SimpleFloatViewManager}, which provides basic float View
|
||||||
|
* creation.
|
||||||
|
*
|
||||||
|
* An instance of this class is meant to be passed to the methods
|
||||||
|
* {@link DragSortListView#setTouchListener()} and
|
||||||
|
* {@link DragSortListView#setFloatViewManager()} of your
|
||||||
|
* {@link DragSortListView} instance.
|
||||||
|
*/
|
||||||
|
public class DragSortController extends SimpleFloatViewManager implements View.OnTouchListener,
|
||||||
|
GestureDetector.OnGestureListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drag init mode enum.
|
||||||
|
*/
|
||||||
|
public static final int ON_DOWN = 0;
|
||||||
|
public static final int ON_DRAG = 1;
|
||||||
|
public static final int ON_LONG_PRESS = 2;
|
||||||
|
|
||||||
|
private int mDragInitMode = ON_DOWN;
|
||||||
|
|
||||||
|
private boolean mSortEnabled = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove mode enum.
|
||||||
|
*/
|
||||||
|
public static final int CLICK_REMOVE = 0;
|
||||||
|
public static final int FLING_REMOVE = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current remove mode.
|
||||||
|
*/
|
||||||
|
private int mRemoveMode;
|
||||||
|
|
||||||
|
private boolean mRemoveEnabled = false;
|
||||||
|
private boolean mIsRemoving = false;
|
||||||
|
|
||||||
|
private GestureDetector mDetector;
|
||||||
|
|
||||||
|
private GestureDetector mFlingRemoveDetector;
|
||||||
|
|
||||||
|
private int mTouchSlop;
|
||||||
|
|
||||||
|
public static final int MISS = -1;
|
||||||
|
|
||||||
|
private int mHitPos = MISS;
|
||||||
|
private int mFlingHitPos = MISS;
|
||||||
|
|
||||||
|
private int mClickRemoveHitPos = MISS;
|
||||||
|
|
||||||
|
private int[] mTempLoc = new int[2];
|
||||||
|
|
||||||
|
private int mItemX;
|
||||||
|
private int mItemY;
|
||||||
|
|
||||||
|
private int mCurrX;
|
||||||
|
private int mCurrY;
|
||||||
|
|
||||||
|
private boolean mDragging = false;
|
||||||
|
|
||||||
|
private float mFlingSpeed = 500f;
|
||||||
|
|
||||||
|
private int mDragHandleId;
|
||||||
|
|
||||||
|
private int mClickRemoveId;
|
||||||
|
|
||||||
|
private int mFlingHandleId;
|
||||||
|
private boolean mCanDrag;
|
||||||
|
|
||||||
|
private DragSortListView mDslv;
|
||||||
|
private int mPositionX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls {@link #DragSortController(DragSortListView, int)} with a
|
||||||
|
* 0 drag handle id, FLING_RIGHT_REMOVE remove mode,
|
||||||
|
* and ON_DOWN drag init. By default, sorting is enabled, and
|
||||||
|
* removal is disabled.
|
||||||
|
*
|
||||||
|
* @param dslv The DSLV instance
|
||||||
|
*/
|
||||||
|
public DragSortController(DragSortListView dslv) {
|
||||||
|
this(dslv, 0, ON_DOWN, FLING_REMOVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode,
|
||||||
|
int removeMode) {
|
||||||
|
|
||||||
|
this(dslv, dragHandleId, dragInitMode, removeMode, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode,
|
||||||
|
int removeMode, int clickRemoveId) {
|
||||||
|
|
||||||
|
this(dslv, dragHandleId, dragInitMode, removeMode, clickRemoveId, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, sorting is enabled, and removal is disabled.
|
||||||
|
*
|
||||||
|
* @param dslv The DSLV instance
|
||||||
|
* @param dragHandleId The resource id of the View that represents
|
||||||
|
* the drag handle in a list item.
|
||||||
|
*/
|
||||||
|
public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode,
|
||||||
|
int removeMode, int clickRemoveId, int flingHandleId) {
|
||||||
|
|
||||||
|
super(dslv);
|
||||||
|
mDslv = dslv;
|
||||||
|
mDetector = new GestureDetector(dslv.getContext(), this);
|
||||||
|
mFlingRemoveDetector = new GestureDetector(dslv.getContext(), mFlingRemoveListener);
|
||||||
|
mFlingRemoveDetector.setIsLongpressEnabled(false);
|
||||||
|
mTouchSlop = ViewConfiguration.get(dslv.getContext()).getScaledTouchSlop();
|
||||||
|
mDragHandleId = dragHandleId;
|
||||||
|
mClickRemoveId = clickRemoveId;
|
||||||
|
mFlingHandleId = flingHandleId;
|
||||||
|
setRemoveMode(removeMode);
|
||||||
|
setDragInitMode(dragInitMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getDragInitMode() {
|
||||||
|
return mDragInitMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set how a drag is initiated. Needs to be one of
|
||||||
|
* {@link ON_DOWN}, {@link ON_DRAG}, or {@link ON_LONG_PRESS}.
|
||||||
|
*
|
||||||
|
* @param mode The drag init mode.
|
||||||
|
*/
|
||||||
|
public void setDragInitMode(int mode) {
|
||||||
|
mDragInitMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable/Disable list item sorting. Disabling is useful if only item
|
||||||
|
* removal is desired. Prevents drags in the vertical direction.
|
||||||
|
*
|
||||||
|
* @param enabled Set <code>true</code> to enable list
|
||||||
|
* item sorting.
|
||||||
|
*/
|
||||||
|
public void setSortEnabled(boolean enabled) {
|
||||||
|
mSortEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSortEnabled() {
|
||||||
|
return mSortEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One of {@link CLICK_REMOVE}, {@link FLING_RIGHT_REMOVE},
|
||||||
|
* {@link FLING_LEFT_REMOVE},
|
||||||
|
* {@link SLIDE_RIGHT_REMOVE}, or {@link SLIDE_LEFT_REMOVE}.
|
||||||
|
*/
|
||||||
|
public void setRemoveMode(int mode) {
|
||||||
|
mRemoveMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRemoveMode() {
|
||||||
|
return mRemoveMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable/Disable item removal without affecting remove mode.
|
||||||
|
*/
|
||||||
|
public void setRemoveEnabled(boolean enabled) {
|
||||||
|
mRemoveEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRemoveEnabled() {
|
||||||
|
return mRemoveEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the resource id for the View that represents the drag
|
||||||
|
* handle in a list item.
|
||||||
|
*
|
||||||
|
* @param id An android resource id.
|
||||||
|
*/
|
||||||
|
public void setDragHandleId(int id) {
|
||||||
|
mDragHandleId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the resource id for the View that represents the fling
|
||||||
|
* handle in a list item.
|
||||||
|
*
|
||||||
|
* @param id An android resource id.
|
||||||
|
*/
|
||||||
|
public void setFlingHandleId(int id) {
|
||||||
|
mFlingHandleId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the resource id for the View that represents click
|
||||||
|
* removal button.
|
||||||
|
*
|
||||||
|
* @param id An android resource id.
|
||||||
|
*/
|
||||||
|
public void setClickRemoveId(int id) {
|
||||||
|
mClickRemoveId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets flags to restrict certain motions of the floating View
|
||||||
|
* based on DragSortController settings (such as remove mode).
|
||||||
|
* Starts the drag on the DragSortListView.
|
||||||
|
*
|
||||||
|
* @param position The list item position (includes headers).
|
||||||
|
* @param deltaX Touch x-coord minus left edge of floating View.
|
||||||
|
* @param deltaY Touch y-coord minus top edge of floating View.
|
||||||
|
*
|
||||||
|
* @return True if drag started, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean startDrag(int position, int deltaX, int deltaY) {
|
||||||
|
|
||||||
|
int dragFlags = 0;
|
||||||
|
if (mSortEnabled && !mIsRemoving) {
|
||||||
|
dragFlags |= DragSortListView.DRAG_POS_Y | DragSortListView.DRAG_NEG_Y;
|
||||||
|
}
|
||||||
|
if (mRemoveEnabled && mIsRemoving) {
|
||||||
|
dragFlags |= DragSortListView.DRAG_POS_X;
|
||||||
|
dragFlags |= DragSortListView.DRAG_NEG_X;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDragging = mDslv.startDrag(position - mDslv.getHeaderViewsCount(), dragFlags, deltaX,
|
||||||
|
deltaY);
|
||||||
|
return mDragging;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouch(View v, MotionEvent ev) {
|
||||||
|
if (!mDslv.isDragEnabled() || mDslv.listViewIntercepted()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDetector.onTouchEvent(ev);
|
||||||
|
if (mRemoveEnabled && mDragging && mRemoveMode == FLING_REMOVE) {
|
||||||
|
mFlingRemoveDetector.onTouchEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
int action = ev.getAction() & MotionEvent.ACTION_MASK;
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
mCurrX = (int) ev.getX();
|
||||||
|
mCurrY = (int) ev.getY();
|
||||||
|
break;
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
if (mRemoveEnabled && mIsRemoving) {
|
||||||
|
int x = mPositionX >= 0 ? mPositionX : -mPositionX;
|
||||||
|
int removePoint = mDslv.getWidth() / 2;
|
||||||
|
if (x > removePoint) {
|
||||||
|
mDslv.stopDragWithVelocity(true, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case MotionEvent.ACTION_CANCEL:
|
||||||
|
mIsRemoving = false;
|
||||||
|
mDragging = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides to provide fading when slide removal is enabled.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onDragFloatView(View floatView, Point position, Point touch) {
|
||||||
|
|
||||||
|
if (mRemoveEnabled && mIsRemoving) {
|
||||||
|
mPositionX = position.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the position to start dragging based on the ACTION_DOWN
|
||||||
|
* MotionEvent. This function simply calls
|
||||||
|
* {@link #dragHandleHitPosition(MotionEvent)}. Override
|
||||||
|
* to change drag handle behavior;
|
||||||
|
* this function is called internally when an ACTION_DOWN
|
||||||
|
* event is detected.
|
||||||
|
*
|
||||||
|
* @param ev The ACTION_DOWN MotionEvent.
|
||||||
|
*
|
||||||
|
* @return The list position to drag if a drag-init gesture is
|
||||||
|
* detected; MISS if unsuccessful.
|
||||||
|
*/
|
||||||
|
public int startDragPosition(MotionEvent ev) {
|
||||||
|
return dragHandleHitPosition(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int startFlingPosition(MotionEvent ev) {
|
||||||
|
return mRemoveMode == FLING_REMOVE ? flingHandleHitPosition(ev) : MISS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for the touch of an item's drag handle (specified by
|
||||||
|
* {@link #setDragHandleId(int)}), and returns that item's position
|
||||||
|
* if a drag handle touch was detected.
|
||||||
|
*
|
||||||
|
* @param ev The ACTION_DOWN MotionEvent.
|
||||||
|
* @return The list position of the item whose drag handle was
|
||||||
|
* touched; MISS if unsuccessful.
|
||||||
|
*/
|
||||||
|
public int dragHandleHitPosition(MotionEvent ev) {
|
||||||
|
return viewIdHitPosition(ev, mDragHandleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int flingHandleHitPosition(MotionEvent ev) {
|
||||||
|
return viewIdHitPosition(ev, mFlingHandleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int viewIdHitPosition(MotionEvent ev, int id) {
|
||||||
|
final int x = (int) ev.getX();
|
||||||
|
final int y = (int) ev.getY();
|
||||||
|
|
||||||
|
int touchPos = mDslv.pointToPosition(x, y); // includes headers/footers
|
||||||
|
|
||||||
|
final int numHeaders = mDslv.getHeaderViewsCount();
|
||||||
|
final int numFooters = mDslv.getFooterViewsCount();
|
||||||
|
final int count = mDslv.getCount();
|
||||||
|
|
||||||
|
// We're only interested if the touch was on an
|
||||||
|
// item that's not a header or footer.
|
||||||
|
if (touchPos != AdapterView.INVALID_POSITION && touchPos >= numHeaders
|
||||||
|
&& touchPos < (count - numFooters)) {
|
||||||
|
final View item = mDslv.getChildAt(touchPos - mDslv.getFirstVisiblePosition());
|
||||||
|
final int rawX = (int) ev.getRawX();
|
||||||
|
final int rawY = (int) ev.getRawY();
|
||||||
|
|
||||||
|
View dragBox = id == 0 ? item : (View) item.findViewById(id);
|
||||||
|
if (dragBox != null) {
|
||||||
|
dragBox.getLocationOnScreen(mTempLoc);
|
||||||
|
|
||||||
|
if (rawX > mTempLoc[0] && rawY > mTempLoc[1] &&
|
||||||
|
rawX < mTempLoc[0] + dragBox.getWidth() &&
|
||||||
|
rawY < mTempLoc[1] + dragBox.getHeight()) {
|
||||||
|
|
||||||
|
mItemX = item.getLeft();
|
||||||
|
mItemY = item.getTop();
|
||||||
|
|
||||||
|
return touchPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MISS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onDown(MotionEvent ev) {
|
||||||
|
if (mRemoveEnabled && mRemoveMode == CLICK_REMOVE) {
|
||||||
|
mClickRemoveHitPos = viewIdHitPosition(ev, mClickRemoveId);
|
||||||
|
}
|
||||||
|
|
||||||
|
mHitPos = startDragPosition(ev);
|
||||||
|
if (mHitPos != MISS && mDragInitMode == ON_DOWN) {
|
||||||
|
startDrag(mHitPos, (int) ev.getX() - mItemX, (int) ev.getY() - mItemY);
|
||||||
|
}
|
||||||
|
|
||||||
|
mIsRemoving = false;
|
||||||
|
mCanDrag = true;
|
||||||
|
mPositionX = 0;
|
||||||
|
mFlingHitPos = startFlingPosition(ev);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||||
|
|
||||||
|
if (e1 == null || e2 == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int x1 = (int) e1.getX();
|
||||||
|
final int y1 = (int) e1.getY();
|
||||||
|
final int x2 = (int) e2.getX();
|
||||||
|
final int y2 = (int) e2.getY();
|
||||||
|
final int deltaX = x2 - mItemX;
|
||||||
|
final int deltaY = y2 - mItemY;
|
||||||
|
|
||||||
|
if (mCanDrag && !mDragging && (mHitPos != MISS || mFlingHitPos != MISS)) {
|
||||||
|
if (mHitPos != MISS) {
|
||||||
|
if (mDragInitMode == ON_DRAG && Math.abs(y2 - y1) > mTouchSlop && mSortEnabled) {
|
||||||
|
startDrag(mHitPos, deltaX, deltaY);
|
||||||
|
}
|
||||||
|
else if (mDragInitMode != ON_DOWN && Math.abs(x2 - x1) > mTouchSlop
|
||||||
|
&& mRemoveEnabled)
|
||||||
|
{
|
||||||
|
mIsRemoving = true;
|
||||||
|
startDrag(mFlingHitPos, deltaX, deltaY);
|
||||||
|
}
|
||||||
|
} else if (mFlingHitPos != MISS) {
|
||||||
|
if (Math.abs(x2 - x1) > mTouchSlop && mRemoveEnabled) {
|
||||||
|
mIsRemoving = true;
|
||||||
|
startDrag(mFlingHitPos, deltaX, deltaY);
|
||||||
|
} else if (Math.abs(y2 - y1) > mTouchSlop) {
|
||||||
|
mCanDrag = false; // if started to scroll the list then
|
||||||
|
// don't allow sorting nor fling-removing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLongPress(MotionEvent e) {
|
||||||
|
if (mHitPos != MISS && mDragInitMode == ON_LONG_PRESS) {
|
||||||
|
mDslv.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||||
|
startDrag(mHitPos, mCurrX - mItemX, mCurrY - mItemY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// complete the OnGestureListener interface
|
||||||
|
@Override
|
||||||
|
public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// complete the OnGestureListener interface
|
||||||
|
@Override
|
||||||
|
public boolean onSingleTapUp(MotionEvent ev) {
|
||||||
|
if (mRemoveEnabled && mRemoveMode == CLICK_REMOVE) {
|
||||||
|
if (mClickRemoveHitPos != MISS) {
|
||||||
|
mDslv.removeItem(mClickRemoveHitPos - mDslv.getHeaderViewsCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// complete the OnGestureListener interface
|
||||||
|
@Override
|
||||||
|
public void onShowPress(MotionEvent ev) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
private GestureDetector.OnGestureListener mFlingRemoveListener =
|
||||||
|
new GestureDetector.SimpleOnGestureListener() {
|
||||||
|
@Override
|
||||||
|
public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
|
||||||
|
float velocityY) {
|
||||||
|
if (mRemoveEnabled && mIsRemoving) {
|
||||||
|
int w = mDslv.getWidth();
|
||||||
|
int minPos = w / 5;
|
||||||
|
if (velocityX > mFlingSpeed) {
|
||||||
|
if (mPositionX > -minPos) {
|
||||||
|
mDslv.stopDragWithVelocity(true, velocityX);
|
||||||
|
}
|
||||||
|
} else if (velocityX < -mFlingSpeed) {
|
||||||
|
if (mPositionX < minPos) {
|
||||||
|
mDslv.stopDragWithVelocity(true, velocityX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mIsRemoving = false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,233 @@
|
|||||||
|
package com.mobeta.android.dslv;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import androidx.cursoradapter.widget.CursorAdapter;
|
||||||
|
import android.util.SparseIntArray;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ListAdapter;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A subclass of {@link android.widget.CursorAdapter} that provides
|
||||||
|
* reordering of the elements in the Cursor based on completed
|
||||||
|
* drag-sort operations. The reordering is a simple mapping of
|
||||||
|
* list positions into Cursor positions (the Cursor is unchanged).
|
||||||
|
* To persist changes made by drag-sorts, one can retrieve the
|
||||||
|
* mapping with the {@link #getCursorPositions()} method, which
|
||||||
|
* returns the reordered list of Cursor positions.
|
||||||
|
*
|
||||||
|
* An instance of this class is passed
|
||||||
|
* to {@link DragSortListView#setAdapter(ListAdapter)} and, since
|
||||||
|
* this class implements the {@link DragSortListView.DragSortListener}
|
||||||
|
* interface, it is automatically set as the DragSortListener for
|
||||||
|
* the DragSortListView instance.
|
||||||
|
*/
|
||||||
|
public abstract class DragSortCursorAdapter extends CursorAdapter implements
|
||||||
|
DragSortListView.DragSortListener {
|
||||||
|
|
||||||
|
private static final int REMOVED = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key is ListView position, value is Cursor position
|
||||||
|
*/
|
||||||
|
private SparseIntArray mListMapping = new SparseIntArray();
|
||||||
|
|
||||||
|
private List<Integer> mRemovedCursorPositions = new ArrayList<Integer>();
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public DragSortCursorAdapter(Context context, Cursor c) {
|
||||||
|
super(context, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DragSortCursorAdapter(Context context, Cursor c, boolean autoRequery) {
|
||||||
|
super(context, c, autoRequery);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DragSortCursorAdapter(Context context, Cursor c, int flags) {
|
||||||
|
super(context, c, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swaps Cursor and clears list-Cursor mapping.
|
||||||
|
*
|
||||||
|
* @see android.widget.CursorAdapter#swapCursor(android.database.Cursor)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Cursor swapCursor(Cursor newCursor) {
|
||||||
|
Cursor old = super.swapCursor(newCursor);
|
||||||
|
resetMappings();
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets list-cursor mapping.
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
resetMappings();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetMappings() {
|
||||||
|
mListMapping.clear();
|
||||||
|
mRemovedCursorPositions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getItem(int position) {
|
||||||
|
return super.getItem(mListMapping.get(position, position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return super.getItemId(mListMapping.get(position, position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getDropDownView(int position, View convertView, ViewGroup parent) {
|
||||||
|
return super.getDropDownView(mListMapping.get(position, position), convertView, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
return super.getView(mListMapping.get(position, position), convertView, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On drop, this updates the mapping between Cursor positions
|
||||||
|
* and ListView positions. The Cursor is unchanged. Retrieve
|
||||||
|
* the current mapping with {@link getCursorPositions()}.
|
||||||
|
*
|
||||||
|
* @see DragSortListView.DropListener#drop(int, int)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void drop(int from, int to) {
|
||||||
|
if (from != to) {
|
||||||
|
int cursorFrom = mListMapping.get(from, from);
|
||||||
|
|
||||||
|
if (from > to) {
|
||||||
|
for (int i = from; i > to; i--) {
|
||||||
|
mListMapping.put(i, mListMapping.get(i - 1, i - 1));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = from; i < to; i++) {
|
||||||
|
mListMapping.put(i, mListMapping.get(i + 1, i + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mListMapping.put(to, cursorFrom);
|
||||||
|
|
||||||
|
cleanMapping();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On remove, this updates the mapping between Cursor positions
|
||||||
|
* and ListView positions. The Cursor is unchanged. Retrieve
|
||||||
|
* the current mapping with {@link getCursorPositions()}.
|
||||||
|
*
|
||||||
|
* @see DragSortListView.RemoveListener#remove(int)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void remove(int which) {
|
||||||
|
int cursorPos = mListMapping.get(which, which);
|
||||||
|
if (!mRemovedCursorPositions.contains(cursorPos)) {
|
||||||
|
mRemovedCursorPositions.add(cursorPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
int newCount = getCount();
|
||||||
|
for (int i = which; i < newCount; i++) {
|
||||||
|
mListMapping.put(i, mListMapping.get(i + 1, i + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
mListMapping.delete(newCount);
|
||||||
|
|
||||||
|
cleanMapping();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does nothing. Just completes DragSortListener interface.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void drag(int from, int to) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove unnecessary mappings from sparse array.
|
||||||
|
*/
|
||||||
|
private void cleanMapping() {
|
||||||
|
List<Integer> toRemove = new ArrayList<Integer>();
|
||||||
|
|
||||||
|
final int size = mListMapping.size();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
if (mListMapping.keyAt(i) == mListMapping.valueAt(i)) {
|
||||||
|
toRemove.add(mListMapping.keyAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int position : toRemove) {
|
||||||
|
mListMapping.delete(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return super.getCount() - mRemovedCursorPositions.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Cursor position mapped to by the provided list position
|
||||||
|
* (given all previously handled drag-sort
|
||||||
|
* operations).
|
||||||
|
*
|
||||||
|
* @param position List position
|
||||||
|
*
|
||||||
|
* @return The mapped-to Cursor position
|
||||||
|
*/
|
||||||
|
public int getCursorPosition(int position) {
|
||||||
|
return mListMapping.get(position, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current order of Cursor positions presented by the
|
||||||
|
* list.
|
||||||
|
*/
|
||||||
|
public List<Integer> getCursorPositions() {
|
||||||
|
List<Integer> result = new ArrayList<Integer>();
|
||||||
|
|
||||||
|
for (int i = 0; i < getCount(); i++) {
|
||||||
|
result.add(mListMapping.get(i, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list position mapped to by the provided Cursor position.
|
||||||
|
* If the provided Cursor position has been removed by a drag-sort,
|
||||||
|
* this returns {@link #REMOVED}.
|
||||||
|
*
|
||||||
|
* @param cursorPosition A Cursor position
|
||||||
|
* @return The mapped-to list position or REMOVED
|
||||||
|
*/
|
||||||
|
public int getListPosition(int cursorPosition) {
|
||||||
|
if (mRemovedCursorPositions.contains(cursorPosition)) {
|
||||||
|
return REMOVED;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = mListMapping.indexOfValue(cursorPosition);
|
||||||
|
if (index < 0) {
|
||||||
|
return cursorPosition;
|
||||||
|
} else {
|
||||||
|
return mListMapping.keyAt(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
package com.mobeta.android.dslv;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AbsListView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lightweight ViewGroup that wraps list items obtained from user's
|
||||||
|
* ListAdapter. ItemView expects a single child that has a definite
|
||||||
|
* height (i.e. the child's layout height is not MATCH_PARENT).
|
||||||
|
* The width of
|
||||||
|
* ItemView will always match the width of its child (that is,
|
||||||
|
* the width MeasureSpec given to ItemView is passed directly
|
||||||
|
* to the child, and the ItemView measured width is set to the
|
||||||
|
* child's measured width). The height of ItemView can be anything;
|
||||||
|
* the
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The purpose of this class is to optimize slide
|
||||||
|
* shuffle animations.
|
||||||
|
*/
|
||||||
|
public class DragSortItemView extends ViewGroup {
|
||||||
|
|
||||||
|
private int mGravity = Gravity.TOP;
|
||||||
|
|
||||||
|
public DragSortItemView(Context context) {
|
||||||
|
super(context);
|
||||||
|
|
||||||
|
// always init with standard ListView layout params
|
||||||
|
setLayoutParams(new AbsListView.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
//setClipChildren(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGravity(int gravity) {
|
||||||
|
mGravity = gravity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGravity() {
|
||||||
|
return mGravity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||||
|
final View child = getChildAt(0);
|
||||||
|
|
||||||
|
if (child == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mGravity == Gravity.TOP) {
|
||||||
|
child.layout(0, 0, getMeasuredWidth(), child.getMeasuredHeight());
|
||||||
|
} else {
|
||||||
|
child.layout(0, getMeasuredHeight() - child.getMeasuredHeight(),
|
||||||
|
getMeasuredWidth(), getMeasuredHeight());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||||
|
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
|
|
||||||
|
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||||
|
|
||||||
|
final View child = getChildAt(0);
|
||||||
|
if (child == null) {
|
||||||
|
setMeasuredDimension(0, width);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child.isLayoutRequested()) {
|
||||||
|
// Always let child be as tall as it wants.
|
||||||
|
measureChild(child, widthMeasureSpec,
|
||||||
|
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (heightMode == MeasureSpec.UNSPECIFIED) {
|
||||||
|
ViewGroup.LayoutParams lp = getLayoutParams();
|
||||||
|
|
||||||
|
if (lp.height > 0) {
|
||||||
|
height = lp.height;
|
||||||
|
} else {
|
||||||
|
height = child.getMeasuredHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setMeasuredDimension(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package com.mobeta.android.dslv;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Checkable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lightweight ViewGroup that wraps list items obtained from user's
|
||||||
|
* ListAdapter. ItemView expects a single child that has a definite
|
||||||
|
* height (i.e. the child's layout height is not MATCH_PARENT).
|
||||||
|
* The width of
|
||||||
|
* ItemView will always match the width of its child (that is,
|
||||||
|
* the width MeasureSpec given to ItemView is passed directly
|
||||||
|
* to the child, and the ItemView measured width is set to the
|
||||||
|
* child's measured width). The height of ItemView can be anything;
|
||||||
|
* the
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The purpose of this class is to optimize slide
|
||||||
|
* shuffle animations.
|
||||||
|
*/
|
||||||
|
public class DragSortItemViewCheckable extends DragSortItemView implements Checkable {
|
||||||
|
|
||||||
|
public DragSortItemViewCheckable(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChecked() {
|
||||||
|
View child = getChildAt(0);
|
||||||
|
if (child instanceof Checkable) {
|
||||||
|
return ((Checkable) child).isChecked();
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setChecked(boolean checked) {
|
||||||
|
View child = getChildAt(0);
|
||||||
|
if (child instanceof Checkable) {
|
||||||
|
((Checkable) child).setChecked(checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toggle() {
|
||||||
|
View child = getChildAt(0);
|
||||||
|
if (child instanceof Checkable) {
|
||||||
|
((Checkable) child).toggle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
* Sortable Preference ListView. Allows for sorting items in a view,
|
||||||
|
* and selecting which ones to use.
|
||||||
|
*
|
||||||
|
* Example Usage (In a preference file)
|
||||||
|
*
|
||||||
|
* <com.mobeta.android.demodslv.SortableListPreference
|
||||||
|
* android:defaultValue="@array/pref_name_defaults"
|
||||||
|
* android:entries="@array/pref_name_titles"
|
||||||
|
* android:entryValues="@array/pref_name_values"
|
||||||
|
* android:key="name_order"
|
||||||
|
* android:persistent="true"
|
||||||
|
* android:title="@string/pref_name_selection" />
|
||||||
|
*
|
||||||
|
* Original Source: https://github.com/kd7uiy/drag-sort-listview
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013 The Making of a Ham, http://www.kd7uiy.com
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Code snippets copied from the following sources:
|
||||||
|
* https://gist.github.com/cardil/4754571
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.mobeta.android.dslv;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import androidx.preference.ListPreference;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
|
||||||
|
|
||||||
|
public class DragSortListPreference extends ListPreference {
|
||||||
|
public HashMap<CharSequence, Boolean> getEntryChecked() {
|
||||||
|
return entryChecked;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final HashMap<CharSequence, Boolean> entryChecked;
|
||||||
|
|
||||||
|
public DragSortListPreference(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
setDialogLayoutResource(R.layout.sort_list_array_dialog_preference);
|
||||||
|
entryChecked = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CharSequence[] decodeValue(String input) {
|
||||||
|
if (input == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (input.equals("")) {
|
||||||
|
return new CharSequence[0];
|
||||||
|
}
|
||||||
|
return input.split(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setValueAndEvent(String value) {
|
||||||
|
if (callChangeListener(decodeValue(value))) {
|
||||||
|
setValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object onGetDefaultValue(TypedArray typedArray, int index) {
|
||||||
|
return typedArray.getTextArray(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSetInitialValue(boolean restoreValue,
|
||||||
|
Object rawDefaultValue) {
|
||||||
|
String value;
|
||||||
|
CharSequence[] defaultValue;
|
||||||
|
if (rawDefaultValue == null) {
|
||||||
|
defaultValue = new CharSequence[0];
|
||||||
|
} else {
|
||||||
|
defaultValue = (CharSequence[]) rawDefaultValue;
|
||||||
|
}
|
||||||
|
List<CharSequence> joined = Arrays.asList(defaultValue);
|
||||||
|
String joinedDefaultValue = join(joined);
|
||||||
|
if (restoreValue) {
|
||||||
|
value = getPersistedString(joinedDefaultValue);
|
||||||
|
} else {
|
||||||
|
value = joinedDefaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
setValueAndEvent(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getValueIndex(CharSequence item) {
|
||||||
|
CharSequence[] entryValues = getEntryValues();
|
||||||
|
for (int i = 0; i < entryValues.length; i++) {
|
||||||
|
if (entryValues[i].equals(item)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CharSequence[] restoreEntries() {
|
||||||
|
|
||||||
|
ArrayList<CharSequence> orderedList = new ArrayList<>();
|
||||||
|
|
||||||
|
// Initially populated with all of the values in the determined list.
|
||||||
|
CharSequence[] values = decodeValue(getValue());
|
||||||
|
for (CharSequence value : values) {
|
||||||
|
orderedList.add(value);
|
||||||
|
entryChecked.put(value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This loop sets the default states, and adds to the name list if not
|
||||||
|
// on the list.
|
||||||
|
for (CharSequence value : getEntryValues()) {
|
||||||
|
if (!orderedList.contains(value)) {
|
||||||
|
orderedList.add(value);
|
||||||
|
entryChecked.put(value, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return orderedList.toArray(new CharSequence[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValueTitleIndex(CharSequence item) {
|
||||||
|
CharSequence[] entries = getEntries();
|
||||||
|
for (int i = 0; i < entries.length; i++) {
|
||||||
|
if (entries[i].equals(item)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException(item + " not found in value title list");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Joins array of object to single string by separator
|
||||||
|
* <p>
|
||||||
|
* Credits to kurellajunior on this post
|
||||||
|
* http://snippets.dzone.com/posts/show/91
|
||||||
|
*
|
||||||
|
* @param iterable any kind of iterable ex.: <code>["a", "b", "c"]</code>
|
||||||
|
* @return joined string ex.: <code>"a,b,c"</code>
|
||||||
|
*/
|
||||||
|
protected static String join(Iterable<?> iterable) {
|
||||||
|
Iterator<?> oIter;
|
||||||
|
if (iterable == null || (!(oIter = iterable.iterator()).hasNext()))
|
||||||
|
return "";
|
||||||
|
StringBuilder oBuilder = new StringBuilder(String.valueOf(oIter.next()));
|
||||||
|
while (oIter.hasNext())
|
||||||
|
oBuilder.append(",").append(oIter.next());
|
||||||
|
return oBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void persistStringValue(String value) {
|
||||||
|
persistString(value);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* Sortable Preference ListView. Allows for sorting items in a view,
|
||||||
|
* and selecting which ones to use.
|
||||||
|
*
|
||||||
|
* Example Usage (In a preference file)
|
||||||
|
*
|
||||||
|
* <com.mobeta.android.demodslv.SortableListPreference
|
||||||
|
* android:defaultValue="@array/pref_name_defaults"
|
||||||
|
* android:entries="@array/pref_name_titles"
|
||||||
|
* android:entryValues="@array/pref_name_values"
|
||||||
|
* android:key="name_order"
|
||||||
|
* android:persistent="true"
|
||||||
|
* android:title="@string/pref_name_selection" />
|
||||||
|
*
|
||||||
|
* Original Source: https://github.com/kd7uiy/drag-sort-listview
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013 The Making of a Ham, http://www.kd7uiy.com
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Code snippets copied from the following sources:
|
||||||
|
* https://gist.github.com/cardil/4754571
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.mobeta.android.dslv;
|
||||||
|
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.preference.ListPreference;
|
||||||
|
import androidx.preference.ListPreferenceDialogFragmentCompat;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
|
||||||
|
|
||||||
|
public class DragSortListPreferenceFragment extends ListPreferenceDialogFragmentCompat implements ListPreference.TargetFragment {
|
||||||
|
protected DragSortListView mListView;
|
||||||
|
protected ArrayAdapter<CharSequence> mAdapter;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBindDialogView(View view) {
|
||||||
|
super.onBindDialogView(view);
|
||||||
|
|
||||||
|
mListView = (DragSortListView) view.findViewById(android.R.id.list);
|
||||||
|
mAdapter = new ArrayAdapter<>(mListView.getContext(),
|
||||||
|
R.layout.list_item_checkable, R.id.text);
|
||||||
|
mListView.setAdapter(mAdapter);
|
||||||
|
// This will drop the item in the new location
|
||||||
|
mListView.setDropListener(new DragSortListView.DropListener() {
|
||||||
|
@Override
|
||||||
|
public void drop(int from, int to) {
|
||||||
|
CharSequence item = mAdapter.getItem(from);
|
||||||
|
mAdapter.remove(item);
|
||||||
|
mAdapter.insert(item, to);
|
||||||
|
// Updates checked states
|
||||||
|
mListView.moveCheckState(from, to);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
DragSortListPreference dslp = ((DragSortListPreference)getPreference());
|
||||||
|
CharSequence[] entries = dslp.getEntries();
|
||||||
|
CharSequence[] entryValues = dslp.getEntryValues();
|
||||||
|
if (entries == null || entryValues == null
|
||||||
|
|| entries.length != entryValues.length) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"SortableListPreference requires an entries array and an entryValues "
|
||||||
|
+ "array which are both the same length");
|
||||||
|
}
|
||||||
|
|
||||||
|
CharSequence[] restoredValues = ((DragSortListPreference)getPreference()).restoreEntries();
|
||||||
|
int i = 0;
|
||||||
|
for (CharSequence value : restoredValues) {
|
||||||
|
int index = dslp.getValueIndex(value);
|
||||||
|
if (index >=0) {
|
||||||
|
mAdapter.add(entries[index]);
|
||||||
|
Boolean checked = dslp.getEntryChecked().get(value);
|
||||||
|
if (checked != null && checked.equals(true)) {
|
||||||
|
mListView.setItemChecked(i, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
||||||
|
// must be empty
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDialogClosed(boolean positiveResult) {
|
||||||
|
DragSortListPreference dslp = ((DragSortListPreference)getPreference());
|
||||||
|
|
||||||
|
List<CharSequence> values = new ArrayList<>();
|
||||||
|
|
||||||
|
CharSequence[] entryValues = dslp.getEntryValues();
|
||||||
|
if (positiveResult && entryValues != null) {
|
||||||
|
for (int i = 0; i < entryValues.length; i++) {
|
||||||
|
String val = (String) mAdapter.getItem(i);
|
||||||
|
boolean isChecked = mListView.isItemChecked(i);
|
||||||
|
if (isChecked) {
|
||||||
|
values.add(entryValues[dslp.getValueTitleIndex(val)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String value = DragSortListPreference.join(values);
|
||||||
|
dslp.setValueAndEvent(value);
|
||||||
|
dslp.persistStringValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Preference findPreference(@NonNull CharSequence key) {
|
||||||
|
return getPreference();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
2999
app/src/main/java/com/mobeta/android/dslv/DragSortListView.java
Normal file
2999
app/src/main/java/com/mobeta/android/dslv/DragSortListView.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.mobeta.android.dslv;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import androidx.cursoradapter.widget.CursorAdapter;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
// taken from v4 rev. 10 ResourceCursorAdapter.java
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static library support version of the framework's {@link android.widget.ResourceCursorAdapter}.
|
||||||
|
* Used to write apps that run on platforms prior to Android 3.0. When running
|
||||||
|
* on Android 3.0 or above, this implementation is still used; it does not try
|
||||||
|
* to switch to the framework's implementation. See the framework SDK
|
||||||
|
* documentation for a class overview.
|
||||||
|
*/
|
||||||
|
public abstract class ResourceDragSortCursorAdapter extends DragSortCursorAdapter {
|
||||||
|
private int mLayout;
|
||||||
|
|
||||||
|
private int mDropDownLayout;
|
||||||
|
|
||||||
|
private LayoutInflater mInflater;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor the enables auto-requery.
|
||||||
|
*
|
||||||
|
* @deprecated This option is discouraged, as it results in Cursor queries
|
||||||
|
* being performed on the application's UI thread and thus can cause poor
|
||||||
|
* responsiveness or even Application Not Responding errors. As an alternative,
|
||||||
|
* use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
|
||||||
|
*
|
||||||
|
* @param context The context where the ListView associated with this adapter is running
|
||||||
|
* @param layout resource identifier of a layout file that defines the views
|
||||||
|
* for this list item. Unless you override them later, this will
|
||||||
|
* define both the item views and the drop down views.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public ResourceDragSortCursorAdapter(Context context, int layout, Cursor c) {
|
||||||
|
super(context, c);
|
||||||
|
mLayout = mDropDownLayout = layout;
|
||||||
|
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor with default behavior as per
|
||||||
|
* {@link CursorAdapter#CursorAdapter(Context, Cursor, boolean)}; it is recommended
|
||||||
|
* you not use this, but instead {@link #ResourceCursorAdapter(Context, int, Cursor, int)}.
|
||||||
|
* When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER}
|
||||||
|
* will always be set.
|
||||||
|
*
|
||||||
|
* @param context The context where the ListView associated with this adapter is running
|
||||||
|
* @param layout resource identifier of a layout file that defines the views
|
||||||
|
* for this list item. Unless you override them later, this will
|
||||||
|
* define both the item views and the drop down views.
|
||||||
|
* @param c The cursor from which to get the data.
|
||||||
|
* @param autoRequery If true the adapter will call requery() on the
|
||||||
|
* cursor whenever it changes so the most recent
|
||||||
|
* data is always displayed. Using true here is discouraged.
|
||||||
|
*/
|
||||||
|
public ResourceDragSortCursorAdapter(Context context, int layout, Cursor c,
|
||||||
|
boolean autoRequery) {
|
||||||
|
|
||||||
|
super(context, c, autoRequery);
|
||||||
|
mLayout = mDropDownLayout = layout;
|
||||||
|
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard constructor.
|
||||||
|
*
|
||||||
|
* @param context The context where the ListView associated with this adapter is running
|
||||||
|
* @param layout Resource identifier of a layout file that defines the views
|
||||||
|
* for this list item. Unless you override them later, this will
|
||||||
|
* define both the item views and the drop down views.
|
||||||
|
* @param c The cursor from which to get the data.
|
||||||
|
* @param flags Flags used to determine the behavior of the adapter,
|
||||||
|
* as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
|
||||||
|
*/
|
||||||
|
public ResourceDragSortCursorAdapter(Context context, int layout, Cursor c, int flags) {
|
||||||
|
super(context, c, flags);
|
||||||
|
mLayout = mDropDownLayout = layout;
|
||||||
|
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inflates view(s) from the specified XML file.
|
||||||
|
*
|
||||||
|
* @see android.widget.CursorAdapter#newView(android.content.Context,
|
||||||
|
* android.database.Cursor, ViewGroup)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||||
|
return mInflater.inflate(mLayout, parent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View newDropDownView(Context context, Cursor cursor, ViewGroup parent) {
|
||||||
|
return mInflater.inflate(mDropDownLayout, parent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the layout resource of the item views.</p>
|
||||||
|
*
|
||||||
|
* @param layout the layout resources used to create item views
|
||||||
|
*/
|
||||||
|
public void setViewResource(int layout) {
|
||||||
|
mLayout = layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the layout resource of the drop down views.</p>
|
||||||
|
*
|
||||||
|
* @param dropDownLayout the layout resources used to create drop down views
|
||||||
|
*/
|
||||||
|
public void setDropDownViewResource(int dropDownLayout) {
|
||||||
|
mDropDownLayout = dropDownLayout;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,427 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2006 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.mobeta.android.dslv;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import androidx.cursoradapter.widget.CursorAdapter;
|
||||||
|
import androidx.cursoradapter.widget.SimpleCursorAdapter;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
// taken from sdk/sources/android-16/android/widget/SimpleCursorAdapter.java
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An easy adapter to map columns from a cursor to TextViews or ImageViews
|
||||||
|
* defined in an XML file. You can specify which columns you want, which
|
||||||
|
* views you want to display the columns, and the XML file that defines
|
||||||
|
* the appearance of these views.
|
||||||
|
*
|
||||||
|
* Binding occurs in two phases. First, if a
|
||||||
|
* {@link android.widget.SimpleCursorAdapter.ViewBinder} is available,
|
||||||
|
* {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)}
|
||||||
|
* is invoked. If the returned value is true, binding has occured. If the
|
||||||
|
* returned value is false and the view to bind is a TextView,
|
||||||
|
* {@link #setViewText(TextView, String)} is invoked. If the returned value
|
||||||
|
* is false and the view to bind is an ImageView,
|
||||||
|
* {@link #setViewImage(ImageView, String)} is invoked. If no appropriate
|
||||||
|
* binding can be found, an {@link IllegalStateException} is thrown.
|
||||||
|
*
|
||||||
|
* If this adapter is used with filtering, for instance in an
|
||||||
|
* {@link android.widget.AutoCompleteTextView}, you can use the
|
||||||
|
* {@link android.widget.SimpleCursorAdapter.CursorToStringConverter} and the
|
||||||
|
* {@link android.widget.FilterQueryProvider} interfaces
|
||||||
|
* to get control over the filtering process. You can refer to
|
||||||
|
* {@link #convertToString(android.database.Cursor)} and
|
||||||
|
* {@link #runQueryOnBackgroundThread(CharSequence)} for more information.
|
||||||
|
*/
|
||||||
|
public class SimpleDragSortCursorAdapter extends ResourceDragSortCursorAdapter {
|
||||||
|
/**
|
||||||
|
* A list of columns containing the data to bind to the UI.
|
||||||
|
* This field should be made private, so it is hidden from the SDK.
|
||||||
|
* {@hide}
|
||||||
|
*/
|
||||||
|
protected int[] mFrom;
|
||||||
|
/**
|
||||||
|
* A list of View ids representing the views to which the data must be bound.
|
||||||
|
* This field should be made private, so it is hidden from the SDK.
|
||||||
|
* {@hide}
|
||||||
|
*/
|
||||||
|
protected int[] mTo;
|
||||||
|
|
||||||
|
private int mStringConversionColumn = -1;
|
||||||
|
private CursorToStringConverter mCursorToStringConverter;
|
||||||
|
private ViewBinder mViewBinder;
|
||||||
|
|
||||||
|
String[] mOriginalFrom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor the enables auto-requery.
|
||||||
|
*
|
||||||
|
* @deprecated This option is discouraged, as it results in Cursor queries
|
||||||
|
* being performed on the application's UI thread and thus can cause poor
|
||||||
|
* responsiveness or even Application Not Responding errors. As an alternative,
|
||||||
|
* use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public SimpleDragSortCursorAdapter(Context context, int layout, Cursor c, String[] from,
|
||||||
|
int[] to) {
|
||||||
|
|
||||||
|
super(context, layout, c);
|
||||||
|
mTo = to;
|
||||||
|
mOriginalFrom = from;
|
||||||
|
findColumns(c, from);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard constructor.
|
||||||
|
*
|
||||||
|
* @param context The context where the ListView associated with this
|
||||||
|
* SimpleListItemFactory is running
|
||||||
|
* @param layout resource identifier of a layout file that defines the views
|
||||||
|
* for this list item. The layout file should include at least
|
||||||
|
* those named views defined in "to"
|
||||||
|
* @param c The database cursor. Can be null if the cursor is not available yet.
|
||||||
|
* @param from A list of column names representing the data to bind to the UI. Can be null
|
||||||
|
* if the cursor is not available yet.
|
||||||
|
* @param to The views that should display column in the "from" parameter.
|
||||||
|
* These should all be TextViews. The first N views in this list
|
||||||
|
* are given the values of the first N columns in the from
|
||||||
|
* parameter. Can be null if the cursor is not available yet.
|
||||||
|
* @param flags Flags used to determine the behavior of the adapter,
|
||||||
|
* as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
|
||||||
|
*/
|
||||||
|
public SimpleDragSortCursorAdapter(Context context, int layout, Cursor c, String[] from,
|
||||||
|
int[] to, int flags) {
|
||||||
|
|
||||||
|
super(context, layout, c, flags);
|
||||||
|
mTo = to;
|
||||||
|
mOriginalFrom = from;
|
||||||
|
findColumns(c, from);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds all of the field names passed into the "to" parameter of the
|
||||||
|
* constructor with their corresponding cursor columns as specified in the
|
||||||
|
* "from" parameter.
|
||||||
|
*
|
||||||
|
* Binding occurs in two phases. First, if a
|
||||||
|
* {@link android.widget.SimpleCursorAdapter.ViewBinder} is available,
|
||||||
|
* {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)}
|
||||||
|
* is invoked. If the returned value is true, binding has occured. If the
|
||||||
|
* returned value is false and the view to bind is a TextView,
|
||||||
|
* {@link #setViewText(TextView, String)} is invoked. If the returned value is
|
||||||
|
* false and the view to bind is an ImageView,
|
||||||
|
* {@link #setViewImage(ImageView, String)} is invoked. If no appropriate
|
||||||
|
* binding can be found, an {@link IllegalStateException} is thrown.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if binding cannot occur
|
||||||
|
*
|
||||||
|
* @see android.widget.CursorAdapter#bindView(android.view.View,
|
||||||
|
* android.content.Context, android.database.Cursor)
|
||||||
|
* @see #getViewBinder()
|
||||||
|
* @see #setViewBinder(android.widget.SimpleCursorAdapter.ViewBinder)
|
||||||
|
* @see #setViewImage(ImageView, String)
|
||||||
|
* @see #setViewText(TextView, String)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void bindView(View view, Context context, Cursor cursor) {
|
||||||
|
final ViewBinder binder = mViewBinder;
|
||||||
|
final int count = mTo.length;
|
||||||
|
final int[] from = mFrom;
|
||||||
|
final int[] to = mTo;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
final View v = view.findViewById(to[i]);
|
||||||
|
if (v != null) {
|
||||||
|
boolean bound = false;
|
||||||
|
if (binder != null) {
|
||||||
|
bound = binder.setViewValue(v, cursor, from[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bound) {
|
||||||
|
String text = cursor.getString(from[i]);
|
||||||
|
if (text == null) {
|
||||||
|
text = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v instanceof TextView) {
|
||||||
|
setViewText((TextView) v, text);
|
||||||
|
} else if (v instanceof ImageView) {
|
||||||
|
setViewImage((ImageView) v, text);
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException(v.getClass().getName() + " is not a " +
|
||||||
|
" view that can be bounds by this SimpleCursorAdapter");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link ViewBinder} used to bind data to views.
|
||||||
|
*
|
||||||
|
* @return a ViewBinder or null if the binder does not exist
|
||||||
|
*
|
||||||
|
* @see #bindView(android.view.View, android.content.Context, android.database.Cursor)
|
||||||
|
* @see #setViewBinder(android.widget.SimpleCursorAdapter.ViewBinder)
|
||||||
|
*/
|
||||||
|
public ViewBinder getViewBinder() {
|
||||||
|
return mViewBinder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the binder used to bind data to views.
|
||||||
|
*
|
||||||
|
* @param viewBinder the binder used to bind data to views, can be null to
|
||||||
|
* remove the existing binder
|
||||||
|
*
|
||||||
|
* @see #bindView(android.view.View, android.content.Context, android.database.Cursor)
|
||||||
|
* @see #getViewBinder()
|
||||||
|
*/
|
||||||
|
public void setViewBinder(ViewBinder viewBinder) {
|
||||||
|
mViewBinder = viewBinder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by bindView() to set the image for an ImageView but only if
|
||||||
|
* there is no existing ViewBinder or if the existing ViewBinder cannot
|
||||||
|
* handle binding to an ImageView.
|
||||||
|
*
|
||||||
|
* By default, the value will be treated as an image resource. If the
|
||||||
|
* value cannot be used as an image resource, the value is used as an
|
||||||
|
* image Uri.
|
||||||
|
*
|
||||||
|
* Intended to be overridden by Adapters that need to filter strings
|
||||||
|
* retrieved from the database.
|
||||||
|
*
|
||||||
|
* @param v ImageView to receive an image
|
||||||
|
* @param value the value retrieved from the cursor
|
||||||
|
*/
|
||||||
|
public void setViewImage(ImageView v, String value) {
|
||||||
|
try {
|
||||||
|
v.setImageResource(Integer.parseInt(value));
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
v.setImageURI(Uri.parse(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by bindView() to set the text for a TextView but only if
|
||||||
|
* there is no existing ViewBinder or if the existing ViewBinder cannot
|
||||||
|
* handle binding to a TextView.
|
||||||
|
*
|
||||||
|
* Intended to be overridden by Adapters that need to filter strings
|
||||||
|
* retrieved from the database.
|
||||||
|
*
|
||||||
|
* @param v TextView to receive text
|
||||||
|
* @param text the text to be set for the TextView
|
||||||
|
*/
|
||||||
|
public void setViewText(TextView v, String text) {
|
||||||
|
v.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the index of the column used to get a String representation
|
||||||
|
* of the Cursor.
|
||||||
|
*
|
||||||
|
* @return a valid index in the current Cursor or -1
|
||||||
|
*
|
||||||
|
* @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
|
||||||
|
* @see #setStringConversionColumn(int)
|
||||||
|
* @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
|
||||||
|
* @see #getCursorToStringConverter()
|
||||||
|
*/
|
||||||
|
public int getStringConversionColumn() {
|
||||||
|
return mStringConversionColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the index of the column in the Cursor used to get a String
|
||||||
|
* representation of that Cursor. The column is used to convert the
|
||||||
|
* Cursor to a String only when the current CursorToStringConverter
|
||||||
|
* is null.
|
||||||
|
*
|
||||||
|
* @param stringConversionColumn a valid index in the current Cursor or -1 to use the default
|
||||||
|
* conversion mechanism
|
||||||
|
*
|
||||||
|
* @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
|
||||||
|
* @see #getStringConversionColumn()
|
||||||
|
* @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
|
||||||
|
* @see #getCursorToStringConverter()
|
||||||
|
*/
|
||||||
|
public void setStringConversionColumn(int stringConversionColumn) {
|
||||||
|
mStringConversionColumn = stringConversionColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the converter used to convert the filtering Cursor
|
||||||
|
* into a String.
|
||||||
|
*
|
||||||
|
* @return null if the converter does not exist or an instance of
|
||||||
|
* {@link android.widget.SimpleCursorAdapter.CursorToStringConverter}
|
||||||
|
*
|
||||||
|
* @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
|
||||||
|
* @see #getStringConversionColumn()
|
||||||
|
* @see #setStringConversionColumn(int)
|
||||||
|
* @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
|
||||||
|
*/
|
||||||
|
public CursorToStringConverter getCursorToStringConverter() {
|
||||||
|
return mCursorToStringConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the converter used to convert the filtering Cursor
|
||||||
|
* into a String.
|
||||||
|
*
|
||||||
|
* @param cursorToStringConverter the Cursor to String converter, or
|
||||||
|
* null to remove the converter
|
||||||
|
*
|
||||||
|
* @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
|
||||||
|
* @see #getStringConversionColumn()
|
||||||
|
* @see #setStringConversionColumn(int)
|
||||||
|
* @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
|
||||||
|
*/
|
||||||
|
public void setCursorToStringConverter(CursorToStringConverter cursorToStringConverter) {
|
||||||
|
mCursorToStringConverter = cursorToStringConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a CharSequence representation of the specified Cursor as defined
|
||||||
|
* by the current CursorToStringConverter. If no CursorToStringConverter
|
||||||
|
* has been set, the String conversion column is used instead. If the
|
||||||
|
* conversion column is -1, the returned String is empty if the cursor
|
||||||
|
* is null or Cursor.toString().
|
||||||
|
*
|
||||||
|
* @param cursor the Cursor to convert to a CharSequence
|
||||||
|
*
|
||||||
|
* @return a non-null CharSequence representing the cursor
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CharSequence convertToString(Cursor cursor) {
|
||||||
|
if (mCursorToStringConverter != null) {
|
||||||
|
return mCursorToStringConverter.convertToString(cursor);
|
||||||
|
} else if (mStringConversionColumn > -1) {
|
||||||
|
return cursor.getString(mStringConversionColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.convertToString(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a map from an array of strings to an array of column-id integers in cursor c.
|
||||||
|
* If c is null, the array will be discarded.
|
||||||
|
*
|
||||||
|
* @param c the cursor to find the columns from
|
||||||
|
* @param from the Strings naming the columns of interest
|
||||||
|
*/
|
||||||
|
private void findColumns(Cursor c, String[] from) {
|
||||||
|
if (c != null) {
|
||||||
|
int i;
|
||||||
|
int count = from.length;
|
||||||
|
if (mFrom == null || mFrom.length != count) {
|
||||||
|
mFrom = new int[count];
|
||||||
|
}
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
mFrom[i] = c.getColumnIndexOrThrow(from[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mFrom = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor swapCursor(Cursor c) {
|
||||||
|
// super.swapCursor() will notify observers before we have
|
||||||
|
// a valid mapping, make sure we have a mapping before this
|
||||||
|
// happens
|
||||||
|
findColumns(c, mOriginalFrom);
|
||||||
|
return super.swapCursor(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the cursor and change the column-to-view mappings at the same time.
|
||||||
|
*
|
||||||
|
* @param c The database cursor. Can be null if the cursor is not available yet.
|
||||||
|
* @param from A list of column names representing the data to bind to the UI. Can be null
|
||||||
|
* if the cursor is not available yet.
|
||||||
|
* @param to The views that should display column in the "from" parameter.
|
||||||
|
* These should all be TextViews. The first N views in this list
|
||||||
|
* are given the values of the first N columns in the from
|
||||||
|
* parameter. Can be null if the cursor is not available yet.
|
||||||
|
*/
|
||||||
|
public void changeCursorAndColumns(Cursor c, String[] from, int[] to) {
|
||||||
|
mOriginalFrom = from;
|
||||||
|
mTo = to;
|
||||||
|
// super.changeCursor() will notify observers before we have
|
||||||
|
// a valid mapping, make sure we have a mapping before this
|
||||||
|
// happens
|
||||||
|
findColumns(c, mOriginalFrom);
|
||||||
|
super.changeCursor(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class can be used by external clients of SimpleCursorAdapter
|
||||||
|
* to bind values fom the Cursor to views.
|
||||||
|
*
|
||||||
|
* You should use this class to bind values from the Cursor to views
|
||||||
|
* that are not directly supported by SimpleCursorAdapter or to
|
||||||
|
* change the way binding occurs for views supported by
|
||||||
|
* SimpleCursorAdapter.
|
||||||
|
*
|
||||||
|
* @see SimpleCursorAdapter#bindView(android.view.View, android.content.Context, android.database.Cursor)
|
||||||
|
* @see SimpleCursorAdapter#setViewImage(ImageView, String)
|
||||||
|
* @see SimpleCursorAdapter#setViewText(TextView, String)
|
||||||
|
*/
|
||||||
|
public static interface ViewBinder {
|
||||||
|
/**
|
||||||
|
* Binds the Cursor column defined by the specified index to the specified view.
|
||||||
|
*
|
||||||
|
* When binding is handled by this ViewBinder, this method must return true.
|
||||||
|
* If this method returns false, SimpleCursorAdapter will attempts to handle
|
||||||
|
* the binding on its own.
|
||||||
|
*
|
||||||
|
* @param view the view to bind the data to
|
||||||
|
* @param cursor the cursor to get the data from
|
||||||
|
* @param columnIndex the column at which the data can be found in the cursor
|
||||||
|
*
|
||||||
|
* @return true if the data was bound to the view, false otherwise
|
||||||
|
*/
|
||||||
|
boolean setViewValue(View view, Cursor cursor, int columnIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class can be used by external clients of SimpleCursorAdapter
|
||||||
|
* to define how the Cursor should be converted to a String.
|
||||||
|
*
|
||||||
|
* @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
|
||||||
|
*/
|
||||||
|
public static interface CursorToStringConverter {
|
||||||
|
/**
|
||||||
|
* Returns a CharSequence representing the specified Cursor.
|
||||||
|
*
|
||||||
|
* @param cursor the cursor for which a CharSequence representation
|
||||||
|
* is requested
|
||||||
|
*
|
||||||
|
* @return a non-null CharSequence representing the cursor
|
||||||
|
*/
|
||||||
|
CharSequence convertToString(Cursor cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package com.mobeta.android.dslv;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.ListView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple implementation of the FloatViewManager class. Uses list
|
||||||
|
* items as they appear in the ListView to create the floating View.
|
||||||
|
*/
|
||||||
|
public class SimpleFloatViewManager implements DragSortListView.FloatViewManager {
|
||||||
|
|
||||||
|
private Bitmap mFloatBitmap;
|
||||||
|
|
||||||
|
private ImageView mImageView;
|
||||||
|
|
||||||
|
private int mFloatBGColor = Color.BLACK;
|
||||||
|
|
||||||
|
private ListView mListView;
|
||||||
|
|
||||||
|
public SimpleFloatViewManager(ListView lv) {
|
||||||
|
mListView = lv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBackgroundColor(int color) {
|
||||||
|
mFloatBGColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This simple implementation creates a Bitmap copy of the
|
||||||
|
* list item currently shown at ListView <code>position</code>.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public View onCreateFloatView(int position) {
|
||||||
|
// Guaranteed that this will not be null? I think so. Nope, got
|
||||||
|
// a NullPointerException once...
|
||||||
|
View v = mListView.getChildAt(position + mListView.getHeaderViewsCount()
|
||||||
|
- mListView.getFirstVisiblePosition());
|
||||||
|
|
||||||
|
if (v == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
v.setPressed(false);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
v.setDrawingCacheEnabled(true);
|
||||||
|
mFloatBitmap = Bitmap.createBitmap(v.getDrawingCache());
|
||||||
|
v.setDrawingCacheEnabled(false);
|
||||||
|
|
||||||
|
if (mImageView == null) {
|
||||||
|
mImageView = new ImageView(mListView.getContext());
|
||||||
|
}
|
||||||
|
mImageView.setBackgroundColor(mFloatBGColor);
|
||||||
|
mImageView.setPadding(0, 0, 0, 0);
|
||||||
|
mImageView.setImageBitmap(mFloatBitmap);
|
||||||
|
mImageView.setLayoutParams(new ViewGroup.LayoutParams(v.getWidth(), v.getHeight()));
|
||||||
|
|
||||||
|
return mImageView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDragFloatView(View floatView, Point position, Point touch) {
|
||||||
|
// Do nothing so we have a concrete class
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the Bitmap from the ImageView created in
|
||||||
|
* onCreateFloatView() and tells the system to recycle it.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onDestroyFloatView(View floatView) {
|
||||||
|
((ImageView) floatView).setImageDrawable(null);
|
||||||
|
|
||||||
|
mFloatBitmap.recycle();
|
||||||
|
mFloatBitmap = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,9 @@ import androidx.preference.EditTextPreference;
|
|||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceFragmentCompat;
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
|
|
||||||
|
import com.mobeta.android.dslv.DragSortListPreference;
|
||||||
|
import com.mobeta.android.dslv.DragSortListPreferenceFragment;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -44,15 +47,15 @@ import nodomain.freeyourgadget.gadgetbridge.util.XTimePreferenceFragment;
|
|||||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_ALTITUDE_CALIBRATE;
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_ALTITUDE_CALIBRATE;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AMPM_ENABLED;
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AMPM_ENABLED;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_ANTILOST_ENABLED;
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_ANTILOST_ENABLED;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_SHORT;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_SHORT;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_SHORT;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_LONG;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_LONG;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_LONG;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_DOUBLE;
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_DOUBLE;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_LONG;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_SHORT;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_DOUBLE;
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_DOUBLE;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_LONG;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_SHORT;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_DOUBLE;
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_DOUBLE;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_LONG;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_SHORT;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_BP_CALIBRATE;
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_BP_CALIBRATE;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DATEFORMAT;
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DATEFORMAT;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DISCONNECTNOTIF_NOSHED;
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DISCONNECTNOTIF_NOSHED;
|
||||||
@ -565,6 +568,15 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat {
|
|||||||
if (getFragmentManager() != null) {
|
if (getFragmentManager() != null) {
|
||||||
dialogFragment.show(getFragmentManager(), "androidx.preference.PreferenceFragment.DIALOG");
|
dialogFragment.show(getFragmentManager(), "androidx.preference.PreferenceFragment.DIALOG");
|
||||||
}
|
}
|
||||||
|
} else if (preference instanceof DragSortListPreference) {
|
||||||
|
dialogFragment = new DragSortListPreferenceFragment();
|
||||||
|
Bundle bundle = new Bundle(1);
|
||||||
|
bundle.putString("key", preference.getKey());
|
||||||
|
dialogFragment.setArguments(bundle);
|
||||||
|
dialogFragment.setTargetFragment(this, 0);
|
||||||
|
if (getFragmentManager() != null) {
|
||||||
|
dialogFragment.show(getFragmentManager(), "androidx.preference.PreferenceFragment.DIALOG");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
super.onDisplayPreferenceDialog(preference);
|
super.onDisplayPreferenceDialog(preference);
|
||||||
}
|
}
|
||||||
|
28
app/src/main/res/layout/list_item_checkable.xml
Normal file
28
app/src/main/res/layout/list_item_checkable.xml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.mobeta.android.dslv.CheckableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/drag_handle"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:contentDescription="drag handle"
|
||||||
|
app:srcCompat="@drawable/ic_drag_handle"
|
||||||
|
app:tint="@color/secondarytext" />
|
||||||
|
|
||||||
|
<CheckedTextView
|
||||||
|
android:id="@+id/text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||||
|
|
||||||
|
</com.mobeta.android.dslv.CheckableLinearLayout>
|
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.mobeta.android.dslv.DragSortListView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:dslv="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@android:id/list"
|
||||||
|
android:focusable="false"
|
||||||
|
android:focusableInTouchMode="false"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_margin="3dp"
|
||||||
|
android:choiceMode="multipleChoice"
|
||||||
|
dslv:sort_enabled="true"
|
||||||
|
dslv:remove_enabled="false"
|
||||||
|
dslv:drag_handle_id="@id/drag_handle" />
|
39
app/src/main/res/values/dslv_attrs.xml
Normal file
39
app/src/main/res/values/dslv_attrs.xml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<resources>
|
||||||
|
<declare-styleable name="DragSortListView">
|
||||||
|
<attr name="collapsed_height" format="dimension" />
|
||||||
|
|
||||||
|
<!-- Float view -->
|
||||||
|
<attr name="float_background_color" format="color" />
|
||||||
|
<attr name="float_alpha" format="float" />
|
||||||
|
|
||||||
|
<!-- Animations -->
|
||||||
|
<attr name="remove_animation_duration" format="integer" />
|
||||||
|
<attr name="drop_animation_duration" format="integer" />
|
||||||
|
<attr name="slide_shuffle_speed" format="float" />
|
||||||
|
|
||||||
|
<!-- Remove properties -->
|
||||||
|
<attr name="remove_enabled" format="boolean" />
|
||||||
|
<attr name="remove_mode">
|
||||||
|
<enum name="clickRemove" value="0" />
|
||||||
|
<enum name="flingRemove" value="1" />
|
||||||
|
</attr>
|
||||||
|
<attr name="fling_handle_id" format="integer" />
|
||||||
|
<attr name="click_remove_id" format="integer" />
|
||||||
|
|
||||||
|
<!-- Drag properties -->
|
||||||
|
<attr name="drag_enabled" format="boolean" />
|
||||||
|
<attr name="drag_start_mode">
|
||||||
|
<enum name="onDown" value="0" />
|
||||||
|
<enum name="onMove" value="1" />
|
||||||
|
<enum name="onLongPress" value="2" />
|
||||||
|
</attr>
|
||||||
|
<attr name="drag_handle_id" format="integer" />
|
||||||
|
<attr name="drag_scroll_start" format="float" />
|
||||||
|
<attr name="max_drag_scroll_speed" format="float" />
|
||||||
|
|
||||||
|
<attr name="track_drag_sort" format="boolean" />
|
||||||
|
<attr name="use_default_controller" format="boolean" />
|
||||||
|
<attr name="sort_enabled" format="boolean" />
|
||||||
|
</declare-styleable>
|
||||||
|
</resources>
|
Loading…
Reference in New Issue
Block a user