package com.carouseldemo.controls;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import com.carouseldemo.main.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Bitmap.Config;
import android.graphics.PorterDuff.Mode;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.animation.Transformation;
import android.widget.BaseAdapter;
/**
*
* @author kushnarev
* Carousel class
*/
public class Carousel extends CarouselSpinner implements GestureDetector.OnGestureListener {
// [start] Static private members
/**
* Tag for a class logging
*/
private static final String TAG = Carousel.class.getSimpleName();
/**
* If logging should be inside class
*/
private static final boolean localLOGV = false;
/**
* Default min quantity of images
*/
private static final int MIN_QUANTITY = 3;
/**
* Default max quantity of images
*/
private static final int MAX_QUANTITY = 12;
/**
* Max theta
*/
private static final float MAX_THETA = 15.0f;
/**
* Duration in milliseconds from the start of a scroll during which we're
* unsure whether the user is scrolling or flinging.
*/
private static final int SCROLL_TO_FLING_UNCERTAINTY_TIMEOUT = 250;
// [end]
// [start] Private members
/**
* The info for adapter context menu
*/
private AdapterContextMenuInfo mContextMenuInfo;
/**
* How long the transition animation should run when a child view changes
* position, measured in milliseconds.
*/
private int mAnimationDuration = 900;
/**
* Camera to make 3D rotation
*/
private Camera mCamera = new Camera();
/**
* Sets mSuppressSelectionChanged = false. This is used to set it to false
* in the future. It will also trigger a selection changed.
*/
private Runnable mDisableSuppressSelectionChangedRunnable = new Runnable() {
public void run() {
mSuppressSelectionChanged = false;
selectionChanged();
}
};
/**
* The position of the item that received the user's down touch.
*/
private int mDownTouchPosition;
/**
* The view of the item that received the user's down touch.
*/
private View mDownTouchView;
/**
* Executes the delta rotations from a fling or scroll movement.
*/
private FlingRotateRunnable mFlingRunnable = new FlingRotateRunnable();
/**
* Helper for detecting touch gestures.
*/
private GestureDetector mGestureDetector;
/**
* Gravity for the widget
*/
private int mGravity;
/**
* If true, this onScroll is the first for this user's drag (remember, a
* drag sends many onScrolls).
*/
private boolean mIsFirstScroll;
/**
* Set max qantity of images
*/
private int mMaxQuantity = MAX_QUANTITY;
/**
* Set min quantity of images
*/
private int mMinQuantity = MIN_QUANTITY;
/**
* If true, we have received the "invoke" (center or enter buttons) key
* down. This is checked before we action on the "invoke" key up, and is
* subsequently cleared.
*/
private boolean mReceivedInvokeKeyDown;
/**
* The currently selected item's child.
*/
private View mSelectedChild;
/**
* Whether to continuously callback on the item selected listener during a
* fling.
*/
private boolean mShouldCallbackDuringFling = true;
/**
* Whether to callback when an item that is not selected is clicked.
*/
private boolean mShouldCallbackOnUnselectedItemClick = true;
/**
* When fling runnable runs, it resets this to false. Any method along the
* path until the end of its run() can set this to true to abort any
* remaining fling. For example, if we've reached either the leftmost or
* rightmost item, we will set this to true.
*/
private boolean mShouldStopFling;
/**
* If true, do not callback to item selected listener.
*/
private boolean mSuppressSelectionChanged;
/**
* The axe angle
*/
private float mTheta = (float)(15.0f*(Math.PI/180.0));
/**
* If items should be reflected
*/
private boolean mUseReflection;
// [end]
// [start] Constructors
public Carousel(Context context) {
this(context, null);
}
public Carousel(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public Carousel(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// It's needed to make items with greater value of
// z coordinate to be behind items with lesser z-coordinate
setChildrenDrawingOrderEnabled(true);
// Making user gestures available
mGestureDetector = new GestureDetector(this);
mGestureDetector.setIsLongpressEnabled(true);
// It's needed to apply 3D transforms to items
// before they are drawn
setStaticTransformationsEnabled(true);
// Retrieve settings
TypedArray arr = getContext().obtainStyledAttributes(attrs, R.styleable.Carousel);
mAnimationDuration = arr.getInteger(R.styleable.Carousel_android_animationDuration, 400);
mUseReflection = arr.getBoolean(R.styleable.Carousel_UseReflection, false);
int selectedItem = arr.getInteger(R.styleable.Carousel_SelectedItem, 0);
int imageArrayID = arr.getResourceId(R.styleable.Carousel_Items, -1);
TypedArray images = getResources().obtainTypedArray(imageArrayID);
int min = arr.getInteger(R.styleable.Carousel_minQuantity, MIN_QUANTITY);
int max = arr.getInteger(R.styleable.Carousel_maxQuantity, MAX_QUANTITY);
float mTheta = arr.getFloat(R.styleable.Carousel_maxTheta, MAX_THETA);
if(mTheta > MAX_THETA || mTheta < 0.0f)
mTheta = MAX_THETA;
mMinQuantity = min < MIN_QUANTITY ? MIN_QUANTITY : min;
mMaxQuantity = max > MAX_QUANTITY ? MAX_QUANTITY : max;
if(arr.length() < mMinQuantity || arr.length() > mMaxQuantity)
throw new IllegalArgumentException("Invalid set of items.");
// Initialize image adapter
ImageAdapter adapter = new ImageAdapter(getContext());
adapter.SetImages(images, mUseReflection);
setAdapter(adapter);
if(selectedItem < 0 || selectedItem >= adapter.getCount())
selectedItem = 0;
// next time we go through layout with this value
setNextSelectedPositionInt(selectedItem);
}
// [end]
// [start] View overrides
// [start] These are for use with horizontal scrollbar
/**
* Compute the horizontal extent of the horizontal scrollbar's thumb
* within the horizontal range. This value is used to compute
* the length of the thumb within the scrollbar's track.
*/
@Override
protected int computeHorizontalScrollExtent() {
// Only 1 item is