package com.android.ex.carousel;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.renderscript.Matrix4f;
import android.renderscript.Mesh;
import android.util.Log;
import com.android.ex.carousel.CarouselRS.CarouselCallback;
/**
* CarouselViewHelper wraps all of the threading and event handling of the CarouselView,
* providing a simpler interface. Most users will just need to implement a handful of
* methods to get an application working.
*
*/
public class CarouselViewHelper implements CarouselCallback {
private static final String TAG = "CarouselViewHelper";
private static final int SET_TEXTURE_N = 1;
private static final int SET_DETAIL_TEXTURE_N = 2;
private static final int SET_GEOMETRY_N = 3;
private static final int SET_MATRIX_N = 4;
// This is an ordered list of base message ids to allow removal of a single item from the
// list for a particular card. The implementation currently supports up to a million cards.
private static final int REQUEST_TEXTURE_N = 1000000;
private static final int REQUEST_DETAIL_TEXTURE_N = 2000000;
private static final int REQUEST_GEOMETRY_N = 3000000;
private static final int REQUEST_END = 4000000;
private HandlerThread mHandlerThread;
private Context mContext;
private CarouselView mCarouselView;
private boolean DBG = false;
private long HOLDOFF_DELAY = 100;
private Handler mAsyncHandler; // Background thread handler for reading textures, geometry, etc.
private Handler mSyncHandler; // Synchronous handler for interacting with UI elements.
public static class TextureParameters {
public TextureParameters() { matrix = new Matrix4f(); }
public TextureParameters(Matrix4f _matrix) { matrix = _matrix; }
public Matrix4f matrix;
};
public static class DetailTextureParameters {
public DetailTextureParameters(float textureOffsetX, float textureOffsetY) {
this.textureOffsetX = textureOffsetX;
this.textureOffsetY = textureOffsetY;
this.lineOffsetX = 0.0f;
this.lineOffsetY = 0.0f;
}
public DetailTextureParameters(
float textureOffsetX, float textureOffsetY,
float lineOffsetX, float lineOffsetY) {
this.textureOffsetX = textureOffsetX;
this.textureOffsetY = textureOffsetY;
this.lineOffsetX = lineOffsetX;
this.lineOffsetY = lineOffsetY;
}
public float textureOffsetX;
public float textureOffsetY;
public float lineOffsetX;
public float lineOffsetY;
};
public void setCarouselView(CarouselView carouselView) {
mCarouselView = carouselView;
mCarouselView.setCallback(this);
}
public CarouselViewHelper(Context context, CarouselView carouselView) {
this(context);
setCarouselView(carouselView);
}
public CarouselViewHelper(Context context) {
mContext = context;
mHandlerThread = new HandlerThread(TAG + ".handler");
mHandlerThread.start();
mAsyncHandler = new AsyncHandler(mHandlerThread.getLooper());
mSyncHandler = new SyncHandler(); // runs in calling thread
}
class AsyncHandler extends Handler {
AsyncHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
int id = msg.arg1;
if (id >= mCarouselView.getCardCount()) {
Log.e(TAG, "Index out of range for get, card:" + id);
return;
}
if (msg.what < REQUEST_TEXTURE_N || msg.what > REQUEST_END) {
Log.e(TAG, "Unknown message: " + id);
return;
}
if (msg.what < REQUEST_DETAIL_TEXTURE_N) {
// REQUEST_TEXTURE_N
final Bitmap bitmap = getTexture(id);
if (bitmap != null) {
mSyncHandler.obtainMessage(SET_TEXTURE_N, id, 0, bitmap).sendToTarget();
}
TextureParameters params = getTextureParameters(id);
if (params != null) {
mSyncHandler.obtainMessage(SET_MATRIX_N, id, 0,
params.matrix.getArray()).sendToTarget();
}
} else if (msg.what < REQUEST_GEOMETRY_N) {
// REQUEST_DETAIL_TEXTURE_N
final Bitmap bitmap = getDetailTexture(id);
if (bitmap != null) {
mSyncHandler.obtainMessage(SET_DETAIL_TEXTURE_N, id, 0, bitmap).sendToTarget();
}
} else if (msg.what < REQUEST_END) {
// REQUEST_GEOMETRY_N
Mesh mesh = getGeometry(id);
if (mesh != null) {
mSyncHandler.obtainMessage(SET_GEOMETRY_N, id, 0, mesh).sendToTarget();
}
}
}
};
class SyncHandler extends Handler {
@Override
public void handleMessage(Message msg) {
int id = msg.arg1;
if (id >= mCarouselView.getCardCount()) {
Log.e(TAG, "Index out of range for set, card:" + id);
return;
}
switch (msg.what) {
case SET_TEXTURE_N:
mCarouselView.setTextureForItem(id, (Bitmap) msg.obj);
break;
case SET_DETAIL_TEXTURE_N:
DetailTextureParameters params = getDetailTextureParameters(id);
float x = params != null ? params.textureOffsetX : 0.0f;
float y = params != null ? params.textureOffsetY : 0.0f;
float lx = params != null ? params.lineOffsetX : 0.0f;
float ly = params != null ? params.lineOffsetY : 0.0f;
mCarouselView.setDetailTextureForItem(id, x, y, lx, ly, (Bitmap) msg.obj);
break;
case SET_GEOMETRY_N:
mCarouselView.setGeometryForItem(id, (Mesh) msg.obj);
break;
case SET_MATRIX_N:
mCarouselView.setMatrixForItem(id, (float[]) msg.obj);
break;
}
}
};
/**
* Implement this method if you want to load a texture for
* the given card. Most subclasses will implement this. Note: this will generally
* not be called in the UI thread, so proper locking should be ensured.
*
* @param id of the texture to load
* @return a valid bitmap
*/
public Bitmap getTexture(int id) {
return null;
}
/**
* Implement this method if you want to load a detail texture for
* the given card. Most subclasses will implement this. Note: this will generally
* not be called in the UI thread, so proper locking should be ensured.
*
* @param id
* @return
*/
public Bitmap getDetailTexture(int id) {
return null;
}
/**
* Implement this method if you want to load geometry for the given card. Most subclasses
* will implement this. Note: this will generally not be called in the UI thread,
* so proper locking should be ensured.
*
* @param id
* @return
*/
public Mesh getGeometry(int id) {
return null;
}
/**
* Implement this method if you want custom texture parameters for
* the given id. Note: this will generally
* not be called in the UI thread, so proper locking should be ensured.
*
* @param id
* @return texture parameters
*/
public TextureParameters getTextureParameters(int id) {
return null;
}
/**
* Implement this method if you want custom detail texture parameters for
* the given id. Note: this will generally
* not be called in the UI thread, so proper locking should be ensured.
*
* @param id the id of the texture being requested
* @return detail texture parameters
*/
public DetailTextureParameters getDetailTextureParameters(int id) {
return null;
}
public void onRequestTexture(int id) {
if (DBG) Log.v(TAG, "onRequestTexture(" + id + ")" );
mAsyncHandler.removeMessages(REQUEST_TEXTURE_N + id);
Message message = mAsyncHandler.obtainMessage(REQUEST_TEXTURE_N + id, id, 0);
mAsyncHandler.sendMessageDelayed(message, HOLDOFF_DELAY);
}
public void onInvalidateTexture(final int id) {
if (DBG) Log.v(TAG, "onInvalidateTexture(" + id + ")");
mAsyncHandler.removeMessages(REQUEST_TEXTURE_N + id);
}
public void onRequestGeometry(int id) {
if (DBG) Log.v(TAG, "onRequestGeometry(" + id + ")");
mAsyncHandler.removeMessages(REQUEST_GEOMETRY_N + id);
mAsyncHandler.sendMessage(mAsyncHandler.obtainMessage(REQUEST_GEOMETRY_N + id, id, 0));
}
public void onInvalidateGeometry(int id) {
if (DBG) Log.v(TAG, "onInvalidateGeometry(" + id + ")");
mAsyncHandler.removeMessages(REQUEST_GEOMETRY_N + id);
}
public void onRequestDetailTexture(int id) {
if (DBG) Log.v(TAG, "onRequestDetailTexture(" + id + ")" );
mAsyncHandler.removeMessages(REQUEST_DETAIL_TEXTURE_N + id);
Message message = mAsyncHandler.obtainMessage(REQUEST_DETAIL_TEXTURE_N + id, id, 0);
mAsyncHandler.sendMessageDelayed(message, HOLDOFF_DELAY);
}
public void onInvalidateDetailTexture(int id) {
if (DBG) Log.v(TAG, "onInvalidateDetailTexture(" + id + ")");
mAsyncHandler.removeMessages(REQUEST_DETAIL_TEXTURE_N + id);
}
public void onCardSelected(int n) {
if (DBG) Log.v(TAG, "onCardSelected(" + n + ")");
}
public void onDetailSelected(int n, int x, int y) {
if (DBG) Log.v(TAG, "onDetailSelected(" + n + ", " + x + ", " + y + ")");
}
public void onCardLongPress(int n, int touchPosition[], Rect detailCoordinates) {
if (DBG) Log.v(TAG, "onCardLongPress(" + n + ", (" + touchPosition + "), (" +
detailCoordinates +") )");
}
public void onAnimationStarted() {
}
public void onAnimationFinished(float carouselRotationAngle) {
}
public void onResume() {
mCarouselView.resume();
}
public void onPause() {
mCarouselView.pause();
}
public void onDestroy() {
mHandlerThread.quit();
}
protected Handler getAsyncHandler() {
return mAsyncHandler;
}
protected CarouselView getCarouselView() {
return mCarouselView;
}
}