1 package com.android.ex.carousel; 2 3 import android.content.Context; 4 import android.graphics.Bitmap; 5 import android.graphics.Matrix; 6 import android.graphics.Rect; 7 import android.os.Handler; 8 import android.os.HandlerThread; 9 import android.os.Looper; 10 import android.os.Message; 11 import android.renderscript.Matrix4f; 12 import android.renderscript.Mesh; 13 import android.util.Log; 14 15 import com.android.ex.carousel.CarouselRS.CarouselCallback; 16 17 /** 18 * CarouselViewHelper wraps all of the threading and event handling of the CarouselView, 19 * providing a simpler interface. Most users will just need to implement a handful of 20 * methods to get an application working. 21 * 22 */ 23 public class CarouselViewHelper implements CarouselCallback { 24 private static final String TAG = "CarouselViewHelper"; 25 private static final int SET_TEXTURE_N = 1; 26 private static final int SET_DETAIL_TEXTURE_N = 2; 27 private static final int SET_GEOMETRY_N = 3; 28 private static final int SET_MATRIX_N = 4; 29 30 // This is an ordered list of base message ids to allow removal of a single item from the 31 // list for a particular card. The implementation currently supports up to a million cards. 32 private static final int REQUEST_TEXTURE_N = 1000000; 33 private static final int REQUEST_DETAIL_TEXTURE_N = 2000000; 34 private static final int REQUEST_GEOMETRY_N = 3000000; 35 private static final int REQUEST_END = 4000000; 36 37 private HandlerThread mHandlerThread; 38 private Context mContext; 39 private CarouselView mCarouselView; 40 private boolean DBG = false; 41 private long HOLDOFF_DELAY = 100; 42 private Handler mAsyncHandler; // Background thread handler for reading textures, geometry, etc. 43 private Handler mSyncHandler; // Synchronous handler for interacting with UI elements. 44 45 public static class TextureParameters { 46 public TextureParameters() { matrix = new Matrix4f(); } 47 public TextureParameters(Matrix4f _matrix) { matrix = _matrix; } 48 public Matrix4f matrix; 49 }; 50 51 public static class DetailTextureParameters { 52 public DetailTextureParameters(float textureOffsetX, float textureOffsetY) { 53 this.textureOffsetX = textureOffsetX; 54 this.textureOffsetY = textureOffsetY; 55 this.lineOffsetX = 0.0f; 56 this.lineOffsetY = 0.0f; 57 } 58 public DetailTextureParameters( 59 float textureOffsetX, float textureOffsetY, 60 float lineOffsetX, float lineOffsetY) { 61 this.textureOffsetX = textureOffsetX; 62 this.textureOffsetY = textureOffsetY; 63 this.lineOffsetX = lineOffsetX; 64 this.lineOffsetY = lineOffsetY; 65 } 66 public float textureOffsetX; 67 public float textureOffsetY; 68 public float lineOffsetX; 69 public float lineOffsetY; 70 }; 71 72 public void setCarouselView(CarouselView carouselView) { 73 mCarouselView = carouselView; 74 mCarouselView.setCallback(this); 75 } 76 77 public CarouselViewHelper(Context context, CarouselView carouselView) { 78 this(context); 79 setCarouselView(carouselView); 80 } 81 82 public CarouselViewHelper(Context context) { 83 mContext = context; 84 85 mHandlerThread = new HandlerThread(TAG + ".handler"); 86 mHandlerThread.start(); 87 88 mAsyncHandler = new AsyncHandler(mHandlerThread.getLooper()); 89 mSyncHandler = new SyncHandler(); // runs in calling thread 90 } 91 92 class AsyncHandler extends Handler { 93 AsyncHandler(Looper looper) { 94 super(looper); 95 } 96 97 @Override 98 public void handleMessage(Message msg) { 99 int id = msg.arg1; 100 if (id >= mCarouselView.getCardCount()) { 101 Log.e(TAG, "Index out of range for get, card:" + id); 102 return; 103 } 104 if (msg.what < REQUEST_TEXTURE_N || msg.what > REQUEST_END) { 105 Log.e(TAG, "Unknown message: " + id); 106 return; 107 } 108 if (msg.what < REQUEST_DETAIL_TEXTURE_N) { 109 // REQUEST_TEXTURE_N 110 final Bitmap bitmap = getTexture(id); 111 if (bitmap != null) { 112 mSyncHandler.obtainMessage(SET_TEXTURE_N, id, 0, bitmap).sendToTarget(); 113 } 114 115 TextureParameters params = getTextureParameters(id); 116 if (params != null) { 117 mSyncHandler.obtainMessage(SET_MATRIX_N, id, 0, 118 params.matrix.getArray()).sendToTarget(); 119 } 120 } else if (msg.what < REQUEST_GEOMETRY_N) { 121 // REQUEST_DETAIL_TEXTURE_N 122 final Bitmap bitmap = getDetailTexture(id); 123 if (bitmap != null) { 124 mSyncHandler.obtainMessage(SET_DETAIL_TEXTURE_N, id, 0, bitmap).sendToTarget(); 125 } 126 } else if (msg.what < REQUEST_END) { 127 // REQUEST_GEOMETRY_N 128 Mesh mesh = getGeometry(id); 129 if (mesh != null) { 130 mSyncHandler.obtainMessage(SET_GEOMETRY_N, id, 0, mesh).sendToTarget(); 131 } 132 } 133 } 134 }; 135 136 class SyncHandler extends Handler { 137 @Override 138 public void handleMessage(Message msg) { 139 int id = msg.arg1; 140 if (id >= mCarouselView.getCardCount()) { 141 Log.e(TAG, "Index out of range for set, card:" + id); 142 return; 143 } 144 145 switch (msg.what) { 146 case SET_TEXTURE_N: 147 mCarouselView.setTextureForItem(id, (Bitmap) msg.obj); 148 break; 149 150 case SET_DETAIL_TEXTURE_N: 151 DetailTextureParameters params = getDetailTextureParameters(id); 152 float x = params != null ? params.textureOffsetX : 0.0f; 153 float y = params != null ? params.textureOffsetY : 0.0f; 154 float lx = params != null ? params.lineOffsetX : 0.0f; 155 float ly = params != null ? params.lineOffsetY : 0.0f; 156 mCarouselView.setDetailTextureForItem(id, x, y, lx, ly, (Bitmap) msg.obj); 157 break; 158 159 case SET_GEOMETRY_N: 160 mCarouselView.setGeometryForItem(id, (Mesh) msg.obj); 161 break; 162 163 case SET_MATRIX_N: 164 mCarouselView.setMatrixForItem(id, (float[]) msg.obj); 165 break; 166 } 167 } 168 }; 169 170 /** 171 * Implement this method if you want to load a texture for 172 * the given card. Most subclasses will implement this. Note: this will generally 173 * <b>not</b> be called in the UI thread, so proper locking should be ensured. 174 * 175 * @param id of the texture to load 176 * @return a valid bitmap 177 */ 178 public Bitmap getTexture(int id) { 179 return null; 180 } 181 182 /** 183 * Implement this method if you want to load a detail texture for 184 * the given card. Most subclasses will implement this. Note: this will generally 185 * <b>not</b> be called in the UI thread, so proper locking should be ensured. 186 * 187 * @param id 188 * @return 189 */ 190 public Bitmap getDetailTexture(int id) { 191 return null; 192 } 193 194 /** 195 * Implement this method if you want to load geometry for the given card. Most subclasses 196 * will implement this. Note: this will generally <b>not</b> be called in the UI thread, 197 * so proper locking should be ensured. 198 * 199 * @param id 200 * @return 201 */ 202 public Mesh getGeometry(int id) { 203 return null; 204 } 205 206 /** 207 * Implement this method if you want custom texture parameters for 208 * the given id. Note: this will generally 209 * <b>not</b> be called in the UI thread, so proper locking should be ensured. 210 * 211 * @param id 212 * @return texture parameters 213 */ 214 public TextureParameters getTextureParameters(int id) { 215 return null; 216 } 217 218 /** 219 * Implement this method if you want custom detail texture parameters for 220 * the given id. Note: this will generally 221 * <b>not</b> be called in the UI thread, so proper locking should be ensured. 222 * 223 * @param id the id of the texture being requested 224 * @return detail texture parameters 225 */ 226 public DetailTextureParameters getDetailTextureParameters(int id) { 227 return null; 228 } 229 230 public void onRequestTexture(int id) { 231 if (DBG) Log.v(TAG, "onRequestTexture(" + id + ")" ); 232 mAsyncHandler.removeMessages(REQUEST_TEXTURE_N + id); 233 Message message = mAsyncHandler.obtainMessage(REQUEST_TEXTURE_N + id, id, 0); 234 mAsyncHandler.sendMessageDelayed(message, HOLDOFF_DELAY); 235 } 236 237 public void onInvalidateTexture(final int id) { 238 if (DBG) Log.v(TAG, "onInvalidateTexture(" + id + ")"); 239 mAsyncHandler.removeMessages(REQUEST_TEXTURE_N + id); 240 } 241 242 public void onRequestGeometry(int id) { 243 if (DBG) Log.v(TAG, "onRequestGeometry(" + id + ")"); 244 mAsyncHandler.removeMessages(REQUEST_GEOMETRY_N + id); 245 mAsyncHandler.sendMessage(mAsyncHandler.obtainMessage(REQUEST_GEOMETRY_N + id, id, 0)); 246 } 247 248 public void onInvalidateGeometry(int id) { 249 if (DBG) Log.v(TAG, "onInvalidateGeometry(" + id + ")"); 250 mAsyncHandler.removeMessages(REQUEST_GEOMETRY_N + id); 251 } 252 253 public void onRequestDetailTexture(int id) { 254 if (DBG) Log.v(TAG, "onRequestDetailTexture(" + id + ")" ); 255 mAsyncHandler.removeMessages(REQUEST_DETAIL_TEXTURE_N + id); 256 Message message = mAsyncHandler.obtainMessage(REQUEST_DETAIL_TEXTURE_N + id, id, 0); 257 mAsyncHandler.sendMessageDelayed(message, HOLDOFF_DELAY); 258 } 259 260 public void onInvalidateDetailTexture(int id) { 261 if (DBG) Log.v(TAG, "onInvalidateDetailTexture(" + id + ")"); 262 mAsyncHandler.removeMessages(REQUEST_DETAIL_TEXTURE_N + id); 263 } 264 265 public void onCardSelected(int n) { 266 if (DBG) Log.v(TAG, "onCardSelected(" + n + ")"); 267 } 268 269 public void onDetailSelected(int n, int x, int y) { 270 if (DBG) Log.v(TAG, "onDetailSelected(" + n + ", " + x + ", " + y + ")"); 271 } 272 273 public void onCardLongPress(int n, int touchPosition[], Rect detailCoordinates) { 274 if (DBG) Log.v(TAG, "onCardLongPress(" + n + ", (" + touchPosition + "), (" + 275 detailCoordinates +") )"); 276 } 277 278 public void onAnimationStarted() { 279 280 } 281 282 public void onAnimationFinished(float carouselRotationAngle) { 283 284 } 285 286 public void onResume() { 287 mCarouselView.resume(); 288 } 289 290 public void onPause() { 291 mCarouselView.pause(); 292 } 293 294 public void onDestroy() { 295 mHandlerThread.quit(); 296 } 297 298 protected Handler getAsyncHandler() { 299 return mAsyncHandler; 300 } 301 302 protected CarouselView getCarouselView() { 303 return mCarouselView; 304 } 305 } 306