blob: d7601028a108431c07a326595c22e0022f6400fb [file] [log] [blame]
Adrien Béraud04463092013-05-06 14:17:22 +10001package com.savoirfairelinux.sflphone.model;
alisionfe9cf712013-05-03 17:26:08 -04002
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +10003import java.util.List;
4
alisionfe9cf712013-05-03 17:26:08 -04005import android.content.Context;
6import android.graphics.Canvas;
7import android.graphics.Color;
Adrien Béraud33268882013-05-18 03:41:15 +10008import android.graphics.Paint;
Adrien Béraud6bbce912013-05-24 00:48:13 +10009import android.graphics.Paint.Align;
Adrien Béraud04463092013-05-06 14:17:22 +100010import android.os.Handler;
11import android.os.Message;
alisionfe9cf712013-05-03 17:26:08 -040012import android.util.AttributeSet;
13import android.util.Log;
alision58356b72013-06-03 17:13:36 -040014import android.view.GestureDetector;
15import android.view.GestureDetector.OnGestureListener;
16import android.view.GestureDetector.SimpleOnGestureListener;
alisionfe9cf712013-05-03 17:26:08 -040017import android.view.MotionEvent;
Adrien Béraud04463092013-05-06 14:17:22 +100018import android.view.SurfaceHolder;
19import android.view.SurfaceView;
alisionfe9cf712013-05-03 17:26:08 -040020import android.view.View;
Adrien Béraud04463092013-05-06 14:17:22 +100021import android.view.View.OnTouchListener;
alisionfe9cf712013-05-03 17:26:08 -040022
alision2cb99562013-05-30 17:02:20 -040023public class BubblesView extends SurfaceView implements SurfaceHolder.Callback, OnTouchListener {
24 private static final String TAG = BubblesView.class.getSimpleName();
alisionfe9cf712013-05-03 17:26:08 -040025
alision2cb99562013-05-30 17:02:20 -040026 private BubblesThread thread = null;
27 private BubbleModel model;
alisionfe9cf712013-05-03 17:26:08 -040028
alision2cb99562013-05-30 17:02:20 -040029 private Paint attractor_paint = new Paint();
30 private Paint name_paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Adrien Béraud6bbce912013-05-24 00:48:13 +100031
alision2cb99562013-05-30 17:02:20 -040032 private float density;
33 private float textDensity;
Adrien Béraud33268882013-05-18 03:41:15 +100034
alision2cb99562013-05-30 17:02:20 -040035 private boolean dragging_bubble = false;
Adrien Béraudc9c424d2013-05-30 17:47:35 +100036
alision2cb99562013-05-30 17:02:20 -040037 public BubblesView(Context context, AttributeSet attrs) {
38 super(context, attrs);
alisionfe9cf712013-05-03 17:26:08 -040039
alision2cb99562013-05-30 17:02:20 -040040 density = getResources().getDisplayMetrics().density;
41 textDensity = getResources().getDisplayMetrics().scaledDensity;
Adrien Béraud6bbce912013-05-24 00:48:13 +100042
alision2cb99562013-05-30 17:02:20 -040043 SurfaceHolder holder = getHolder();
44 holder.addCallback(this);
alisionfe9cf712013-05-03 17:26:08 -040045
alision2cb99562013-05-30 17:02:20 -040046 // create thread only; it's started in surfaceCreated()
47 createThread();
alisionfe9cf712013-05-03 17:26:08 -040048
alision2cb99562013-05-30 17:02:20 -040049 setOnTouchListener(this);
50 setFocusable(true);
Adrien Béraud33268882013-05-18 03:41:15 +100051
alision2cb99562013-05-30 17:02:20 -040052 attractor_paint.setColor(Color.RED);
53 // attractor_paint.set
54 name_paint.setTextSize(18 * textDensity);
55 name_paint.setColor(0xFF303030);
56 name_paint.setTextAlign(Align.CENTER);
alision58356b72013-06-03 17:13:36 -040057
58 gDetector = new GestureDetector(getContext(), new MyOnGestureListener());
alision2cb99562013-05-30 17:02:20 -040059 }
alisionfe9cf712013-05-03 17:26:08 -040060
alision2cb99562013-05-30 17:02:20 -040061 private void createThread() {
62 if (thread != null)
63 return;
64 thread = new BubblesThread(getHolder(), getContext(), new Handler() {
65 @Override
66 public void handleMessage(Message m) {
67 /*
68 * mStatusText.setVisibility(m.getData().getInt("viz")); mStatusText.setText(m.getData().getString("text"));
69 */
70 }
71 });
72 if (model != null)
73 thread.setModel(model);
74 }
alisionfe9cf712013-05-03 17:26:08 -040075
alision2cb99562013-05-30 17:02:20 -040076 public void setModel(BubbleModel model) {
77 this.model = model;
78 thread.setModel(model);
79 }
alisionfe9cf712013-05-03 17:26:08 -040080
alision2cb99562013-05-30 17:02:20 -040081 /*
82 * @Override public void onWindowFocusChanged(boolean hasWindowFocus) { if (!hasWindowFocus) { thread.pause(); } }
83 */
alisionfe9cf712013-05-03 17:26:08 -040084
alision2cb99562013-05-30 17:02:20 -040085 @Override
86 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
87 Log.w(TAG, "surfaceChanged " + width + "-" + height);
88 thread.setSurfaceSize(width, height);
89 }
alisionfe9cf712013-05-03 17:26:08 -040090
alision2cb99562013-05-30 17:02:20 -040091 /*
92 * Callback invoked when the Surface has been created and is ready to be used.
93 */
94 @Override
95 public void surfaceCreated(SurfaceHolder holder) {
96 // start the thread here so that we don't busy-wait in run()
97 // waiting for the surface to be created
98 createThread();
alisionfe9cf712013-05-03 17:26:08 -040099
alision2cb99562013-05-30 17:02:20 -0400100 Log.w(TAG, "surfaceCreated");
101 thread.setRunning(true);
102 thread.start();
103 }
alisionfe9cf712013-05-03 17:26:08 -0400104
alision2cb99562013-05-30 17:02:20 -0400105 /*
106 * Callback invoked when the Surface has been destroyed and must no longer be touched. WARNING: after this method returns, the Surface/Canvas must
107 * never be touched again!
108 */
109 @Override
110 public void surfaceDestroyed(SurfaceHolder holder) {
111 // we have to tell thread to shut down & wait for it to finish, or else
112 // it might touch the Surface after we return and explode
113 Log.w(TAG, "surfaceDestroyed");
114 boolean retry = true;
115 thread.setRunning(false);
116 while (retry) {
117 try {
118 thread.join();
119 retry = false;
120 } catch (InterruptedException e) {
121 }
122 }
123 thread = null;
124 }
alisionfe9cf712013-05-03 17:26:08 -0400125
alision58356b72013-06-03 17:13:36 -0400126 private GestureDetector gDetector;
127
alision2cb99562013-05-30 17:02:20 -0400128 @Override
129 public boolean onTouch(View v, MotionEvent event) {
130 Log.w(TAG, "onTouch " + event.getAction());
alision58356b72013-06-03 17:13:36 -0400131
alision2cb99562013-05-30 17:02:20 -0400132 int action = event.getActionMasked();
alision58356b72013-06-03 17:13:36 -0400133//
134// synchronized (model) {
135// List<Bubble> bubbles = model.getBubbles();
136// final int n_bubbles = bubbles.size();
137//
138// if (action == MotionEvent.ACTION_DOWN) {
139// for (int i = 0; i < n_bubbles; i++) {
140// Bubble b = bubbles.get(i);
141// if (b.intersects(event.getX(), event.getY())) {
142// b.dragged = true;
143// b.last_drag = System.nanoTime();
144// b.setPos(event.getX(), event.getY());
145// b.target_scale = .8f;
146// dragging_bubble = true;
147// }
148// }
149// } else if (action == MotionEvent.ACTION_MOVE) {
150// long now = System.nanoTime();
151// for (int i = 0; i < n_bubbles; i++) {
152// Bubble b = bubbles.get(i);
153// if (b.dragged) {
154// float x = event.getX(), y = event.getY();
155// float dt = (float) ((now - b.last_drag) / 1000000000.);
156// float dx = x - b.getPosX(), dy = y - b.getPosY();
157// b.last_drag = now;
158//
159// b.setPos(event.getX(), event.getY());
160// /*
161// * int hn = event.getHistorySize() - 2; Log.w(TAG, "event.getHistorySize() : " + event.getHistorySize()); if(hn > 0) { float
162// * dx = x-event.getHistoricalX(hn); float dy = y-event.getHistoricalY(hn); float dt = event.getHistoricalEventTime(hn)/1000.f;
163// */
164// b.speed.x = dx / dt;
165// b.speed.y = dy / dt;
166// // Log.w(TAG, "onTouch dx:" + b.speed.x + " dy:" + b.speed.y);
167// // }
168// return true;
169// }
170// }
171// } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
172// for (int i = 0; i < n_bubbles; i++) {
173// Bubble b = bubbles.get(i);
174// if (b.dragged) {
175// b.dragged = false;
176// b.target_scale = 1.f;
177// }
178// }
179// dragging_bubble = false;
180// }
181// }
182//
183// return true;
184
185 if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
alision2cb99562013-05-30 17:02:20 -0400186 List<Bubble> bubbles = model.getBubbles();
187 final int n_bubbles = bubbles.size();
alision58356b72013-06-03 17:13:36 -0400188 for (int i = 0; i < n_bubbles; i++) {
189 Bubble b = bubbles.get(i);
190 if (b.dragged) {
191 b.dragged = false;
192 b.target_scale = 1.f;
alision2cb99562013-05-30 17:02:20 -0400193 }
alision2cb99562013-05-30 17:02:20 -0400194 }
alision58356b72013-06-03 17:13:36 -0400195 dragging_bubble = false;
alision2cb99562013-05-30 17:02:20 -0400196 }
alision58356b72013-06-03 17:13:36 -0400197
198 return gDetector.onTouchEvent(event);
alision2cb99562013-05-30 17:02:20 -0400199 }
alisionfe9cf712013-05-03 17:26:08 -0400200
alision2cb99562013-05-30 17:02:20 -0400201 public boolean isDraggingBubble() {
202 return dragging_bubble;
203 }
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000204
alision2cb99562013-05-30 17:02:20 -0400205 class BubblesThread extends Thread {
206 private boolean running = false;
207 private SurfaceHolder surfaceHolder;
alisionfe9cf712013-05-03 17:26:08 -0400208
alision2cb99562013-05-30 17:02:20 -0400209 BubbleModel model = null;
alisionfe9cf712013-05-03 17:26:08 -0400210
alision2cb99562013-05-30 17:02:20 -0400211 public BubblesThread(SurfaceHolder holder, Context context, Handler handler) {
212 surfaceHolder = holder;
213 }
alisionfe9cf712013-05-03 17:26:08 -0400214
alision2cb99562013-05-30 17:02:20 -0400215 public void setModel(BubbleModel model) {
216 this.model = model;
217 }
alisionfe9cf712013-05-03 17:26:08 -0400218
alision2cb99562013-05-30 17:02:20 -0400219 @Override
220 public void run() {
221 while (running) {
222 Canvas c = null;
223 try {
224 c = surfaceHolder.lockCanvas(null);
Adrien Béraud7ed23dc2013-05-06 16:27:24 +1000225
alision2cb99562013-05-30 17:02:20 -0400226 // for the case the surface is destroyed while already in the loop
227 if (c == null || model == null)
228 continue;
Adrien Béraud7ed23dc2013-05-06 16:27:24 +1000229
alision2cb99562013-05-30 17:02:20 -0400230 synchronized (surfaceHolder) {
231 // Log.w(TAG, "Thread doDraw");
232 model.update();
233 doDraw(c);
234 }
235 } finally {
236 if (c != null)
237 surfaceHolder.unlockCanvasAndPost(c);
238 }
239 }
240 }
Adrien Béraud04463092013-05-06 14:17:22 +1000241
alision2cb99562013-05-30 17:02:20 -0400242 public void setRunning(boolean b) {
243 running = b;
244 }
Adrien Béraud04463092013-05-06 14:17:22 +1000245
alision2cb99562013-05-30 17:02:20 -0400246 public void setSurfaceSize(int width, int height) {
247 synchronized (surfaceHolder) {
248 if (model != null) {
249 model.width = width;
250 model.height = height;
251 }
252 }
253 }
Adrien Béraud04463092013-05-06 14:17:22 +1000254
alision2cb99562013-05-30 17:02:20 -0400255 /**
256 * I got multiple IndexOutOfBoundsException, when switching calls. //FIXME
257 *
258 * @param canvas
259 */
260 private void doDraw(Canvas canvas) {
261 canvas.drawColor(Color.WHITE);
Adrien Béraud04463092013-05-06 14:17:22 +1000262
alision2cb99562013-05-30 17:02:20 -0400263 synchronized (model) {
264 List<Bubble> bubbles = model.getBubbles();
265 List<Attractor> attractors = model.getAttractors();
266 try {
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000267
alision2cb99562013-05-30 17:02:20 -0400268 for (int i = 0, n = attractors.size(); i < n; i++) {
269 Attractor a = attractors.get(i);
270 canvas.drawBitmap(a.getBitmap(), null, a.getBounds(), null);
271 }
Adrien Béraud33268882013-05-18 03:41:15 +1000272
alision2cb99562013-05-30 17:02:20 -0400273 for (int i = 0, n = bubbles.size(); i < n; i++) {
274 Bubble b = bubbles.get(i);
275 canvas.drawBitmap(b.getBitmap(), null, b.getBounds(), null);
276 canvas.drawText(b.contact.getmDisplayName(), b.getPosX(), b.getPosY() - 50 * density, name_paint);
277 }
278
279 } catch (IndexOutOfBoundsException e) {
280 Log.e(TAG, e.toString());
281 }
282 }
283 }
284 }
Adrien Béraud04463092013-05-06 14:17:22 +1000285
alision58356b72013-06-03 17:13:36 -0400286 class MyOnGestureListener implements OnGestureListener {
287 @Override
288 public boolean onDown(MotionEvent event) {
289 List<Bubble> bubbles = model.getBubbles();
290 final int n_bubbles = bubbles.size();
291 Log.d("Main", "onDown");
292 for (int i = 0; i < n_bubbles; i++) {
293 Bubble b = bubbles.get(i);
294 if (b.intersects(event.getX(), event.getY())) {
295 b.dragged = true;
296 b.last_drag = System.nanoTime();
297 b.setPos(event.getX(), event.getY());
298 b.target_scale = .8f;
299 dragging_bubble = true;
300 }
301 }
302 return true;
303 }
304
305 @Override
306 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
307 Log.d("Main", "onFling");
308 return true;
309 }
310
311 @Override
312 public void onLongPress(MotionEvent e) {
313 Log.d("Main", "onLongPress");
314 }
315
316 @Override
317 public boolean onScroll(MotionEvent e1, MotionEvent event, float distanceX, float distanceY) {
318 Log.d("Main", "onScroll");
319 List<Bubble> bubbles = model.getBubbles();
320 final int n_bubbles = bubbles.size();
321 long now = System.nanoTime();
322 for (int i = 0; i < n_bubbles; i++) {
323 Bubble b = bubbles.get(i);
324 if (b.dragged) {
325 float x = event.getX(), y = event.getY();
326 float dt = (float) ((now - b.last_drag) / 1000000000.);
327 float dx = x - b.getPosX(), dy = y - b.getPosY();
328 b.last_drag = now;
329
330 b.setPos(event.getX(), event.getY());
331 /*
332 * int hn = event.getHistorySize() - 2; Log.w(TAG, "event.getHistorySize() : " + event.getHistorySize()); if(hn > 0) { float dx =
333 * x-event.getHistoricalX(hn); float dy = y-event.getHistoricalY(hn); float dt = event.getHistoricalEventTime(hn)/1000.f;
334 */
335 b.speed.x = dx / dt;
336 b.speed.y = dy / dt;
337 // Log.w(TAG, "onTouch dx:" + b.speed.x + " dy:" + b.speed.y);
338 // }
339 return true;
340 }
341 }
342 return true;
343 }
344
345 @Override
346 public void onShowPress(MotionEvent e) {
347 Log.d("Main", "onShowPress");
348
349 }
350
351 @Override
352 public boolean onSingleTapUp(MotionEvent e) {
353 Log.d("Main", "onSingleTapUp");
354 return true;
355 }
356 }
357
alisionfe9cf712013-05-03 17:26:08 -0400358}