package ca.uwaterloo.mainscreencontrols;
import java.util.ArrayList;
import java.util.Collections;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SpinnerAdapter;
public abstract class SpinningMenuSpinner extends SpinningMenuAdapter<SpinnerAdapter> {
SpinnerAdapter mAdapter;
int mHeightMeasureSpec;
int mWidthMeasureSpec;
boolean mBlockLayoutRequests;
int mSelectionLeftPadding = 0;
int mSelectionTopPadding = 0;
int mSelectionRightPadding = 0;
int mSelectionBottomPadding = 0;
final Rect mSpinnerPadding = new Rect();
final RecycleBin mRecycler = new RecycleBin();
private DataSetObserver mDataSetObserver;
public SpinningMenuSpinner(Context context) {
super(context);
initSpinningMenuSpinner();
}
public SpinningMenuSpinner(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SpinningMenuSpinner(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initSpinningMenuSpinner();
}
private void initSpinningMenuSpinner() {
setFocusable(true);
setWillNotDraw(false);
}
@Override
public SpinnerAdapter getAdapter() {
return mAdapter;
}
@Override
public void setAdapter(SpinnerAdapter adapter) {
if (null != mAdapter) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
resetList();
}
mAdapter = adapter;
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
if (mAdapter != null) {
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
int position = mItemCount > 0 ? 0 : INVALID_POSITION;
setSelectedPositionInt(position);
setNextSelectedPositionInt(position);
if (mItemCount == 0) {
// Nothing selected
checkSelectionChanged();
}
} else {
checkFocus();
resetList();
// Nothing selected
checkSelectionChanged();
}
requestLayout();
}
@Override
public View getSelectedView() {
if ((mItemCount > 0) && (mSelectedPosition >= 0)) {
return getChildAt(mSelectedPosition - mFirstPosition);
} else {
return null;
}
}
public void setSelection(int position, boolean animate) {
// Animate only if requested position is already on screen somewhere
boolean shouldAnimate = animate && (mFirstPosition <= position) &&
(position <= ((mFirstPosition + getChildCount()) - 1));
setSelectionInt(position, shouldAnimate);
}
void setSelectionInt(int position, boolean animate) {
if (position != mOldSelectedPosition) {
mBlockLayoutRequests = true;
int delta = position - mSelectedPosition;
setNextSelectedPositionInt(position);
layout(delta, animate);
mBlockLayoutRequests = false;
}
}
abstract void layout(int delta, boolean animate);
@Override
public void setSelection(int position) {
setSelectionInt(position, false);
}
void resetList() {
mDataChanged = false;
mNeedSync = false;
removeAllViewsInLayout();
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
setSelectedPositionInt(INVALID_POSITION);
setNextSelectedPositionInt(INVALID_POSITION);
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize;
int heightSize;
mSpinnerPadding.left = getPaddingLeft() > mSelectionLeftPadding ? getPaddingLeft()
: mSelectionLeftPadding;
mSpinnerPadding.top = getPaddingTop() > mSelectionTopPadding ? getPaddingTop()
: mSelectionTopPadding;
mSpinnerPadding.right = getPaddingRight() > mSelectionRightPadding ? getPaddingRight()
: mSelectionRightPadding;
mSpinnerPadding.bottom = getPaddingBottom() > mSelectionBottomPadding ? getPaddingBottom()
: mSelectionBottomPadding;
if (mDataChanged) {
handleDataChanged();
}
int preferredHeight = 0;
int preferredWidth = 0;
boolean needsMeasuring = true;
int selectedPosition = getSelectedItemPosition();
if ((selectedPosition >= 0) && (mAdapter != null) && (selectedPosition < mAdapter.getCount())) {
// Try looking in the recycler. (Maybe we were measured once already)
View view = mRecycler.get(selectedPosition);
if (view == null) {
// Make a new one
view = mAdapter.getView(selectedPosition, null, this);
}
if (view != null) {
// Put in recycler for re-measuring and/or layout
mRecycler.put(selectedPosition, view);
}
if (view != null) {
if (view.getLayoutParams() == null) {
mBlockLayoutRequests = true;
view.setLayoutParams(generateDefaultLayoutParams());
mBlockLayoutRequests = false;
}
measureChild(view, widthMeasureSpec, heightMeasureSpec);
preferredHeight = getChildHeight(view) + mSpinnerPadding.top + mSpinnerPadding.bottom;
preferredWidth = getChildWidth(view) + mSpinnerPadding.left + mSpinnerPadding.right;
needsMeasuring = false;
}
}
if (needsMeasuring) {
// No views -- just use padding
preferredHeight = mSpinnerPadding.top + mSpinnerPadding.bottom;
if (widthMode == MeasureSpec.UNSPECIFIED) {
preferredWidth = mSpinnerPadding.left + mSpinnerPadding.right;
}
}
preferredHeight = Math.max(preferredHeight, getSuggestedMinimumHeight());
preferredWidth = Math.max(preferredWidth, getSuggestedMinimumWidth());
heightSize = resolveSize(preferredHeight, heightMeasureSpec);
widthSize = resolveSize(preferredWidth, widthMeasureSpec);
setMeasuredDimension(widthSize, heightSize);
mHeightMeasureSpec = heightMeasureSpec;
mWidthMeasureSpec = widthMeasureSpec;
}
int getChildHeight(View child) {
return child.getMeasuredHeight();
}
int getChildWidth(View child) {
return child.getMeasuredWidth();
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new SpinningMenu.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
void recycleAllViews() {
final int childCount = getChildCount();
final SpinningMenuSpinner.RecycleBin recycleBin = mRecycler;
final int position = mFirstPosition;
// All views go in recycler
for (int i = 0; i < childCount; i++) {
View v = getChildAt(i);
int index = position + i;
recycleBin.put(index, v);
}
}
@Override
public void requestLayout() {
if (!mBlockLayoutRequests) {
super.requestLayout();
}
}
@Override
public int getCount() {
return mItemCount;
}
public int pointToPosition(int x, int y) {
ArrayList<SpinningMenuItem> fitting = new ArrayList<SpinningMenuItem>();
for(int i = 0; i < mAdapter.getCount(); i++){
SpinningMenuItem item = (SpinningMenuItem)getChildAt(i);
Matrix mm = item.getCIMatrix();
float[] pts = new float[3];
pts[0] = item.getLeft();
pts[1] = item.getTop();
pts[2] = 0;
if (mm!=null) {
mm.mapPoints(pts);
}
int mappedLeft = (int)pts[0];
int mappedTop = (int)pts[1];
pts[0] = item.getRight();
pts[1] = item.getBottom();
pts[2] = 0;
if (mm!=null) {
mm.mapPoints(pts);
}
int mappedRight = (int)pts[0];
int mappedBottom = (int)pts[1];
if((mappedLeft < x) && ((mappedRight > x) & (mappedTop < y)) && (mappedBottom > y)) {
fitting.add(item);
}
}
Collections.sort(fitting);
if(fitting.size() != 0) {
return fitting.get(0).getIndex();
} else {
return mSelectedPosition;
}
}
static class SavedState extends BaseSavedState {
long selectedId;
int position;
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
selectedId = in.readLong();
position = in.readInt();
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeLong(selectedId);
out.writeInt(position);
}
@Override
public String toString() {
return "AbsSpinner.SavedState{"
+ Integer.toHexString(System.identityHashCode(this))
+ " selectedId=" + selectedId
+ " position=" + position + "}";
}
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.selectedId = getSelectedItemId();
if (ss.selectedId >= 0) {
ss.position = getSelectedItemPosition();
} else {
ss.position = INVALID_POSITION;
}
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
if (ss.selectedId >= 0) {
mDataChanged = true;
mNeedSync = true;
mSyncRowId = ss.selectedId;
mSyncPosition = ss.position;
mSyncMode = SYNC_SELECTED_POSITION;
requestLayout();
}
}
class RecycleBin {
private final SparseArray<View> mScrapHeap = new SparseArray<View>();
public void put(int position, View v) {
mScrapHeap.put(position, v);
}
View get(int position) {
// System.out.print("Looking for " + position);
View result = mScrapHeap.get(position);
if (result != null) {
// System.out.println(" HIT");
mScrapHeap.delete(position);
} else {
// System.out.println(" MISS");
}
return result;
}
void clear() {
final SparseArray<View> scrapHeap = mScrapHeap;
final int count = scrapHeap.size();
for (int i = 0; i < count; i++) {
final View view = scrapHeap.valueAt(i);
if (view != null) {
removeDetachedView(view, true);
}
}
scrapHeap.clear();
}
}
}