| 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 |
| * <b>not</b> 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 |
| * <b>not</b> 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 <b>not</b> 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 |
| * <b>not</b> 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 |
| * <b>not</b> 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; |
| } |
| } |