1package com.android.ex.carousel;
2
3import android.content.Context;
4import android.graphics.Bitmap;
5import android.graphics.Matrix;
6import android.graphics.Rect;
7import android.os.Handler;
8import android.os.HandlerThread;
9import android.os.Looper;
10import android.os.Message;
11import android.renderscript.Matrix4f;
12import android.renderscript.Mesh;
13import android.util.Log;
14
15import 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 */
23public 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