blob: 12432751d753ebf4771a06358fddd9f22f742ba7 [file] [log] [blame]
Adrien Béraud04463092013-05-06 14:17:22 +10001package com.savoirfairelinux.sflphone.model;
alisionfe9cf712013-05-03 17:26:08 -04002
3import android.content.Context;
4import android.graphics.Canvas;
5import android.graphics.Color;
Adrien Béraud33268882013-05-18 03:41:15 +10006import android.graphics.Paint;
Adrien Béraud6bbce912013-05-24 00:48:13 +10007import android.graphics.Paint.Align;
8import android.graphics.RectF;
Adrien Béraud04463092013-05-06 14:17:22 +10009import android.os.Handler;
10import android.os.Message;
alisionfe9cf712013-05-03 17:26:08 -040011import android.util.AttributeSet;
12import android.util.Log;
13import android.view.MotionEvent;
Adrien Béraud04463092013-05-06 14:17:22 +100014import android.view.SurfaceHolder;
15import android.view.SurfaceView;
alisionfe9cf712013-05-03 17:26:08 -040016import android.view.View;
Adrien Béraud04463092013-05-06 14:17:22 +100017import android.view.View.OnTouchListener;
alisionfe9cf712013-05-03 17:26:08 -040018
Adrien Béraud04463092013-05-06 14:17:22 +100019public class BubblesView extends SurfaceView implements SurfaceHolder.Callback, OnTouchListener
20{
21 private static final String TAG = BubblesView.class.getSimpleName();
alisionfe9cf712013-05-03 17:26:08 -040022
Adrien Béraud04463092013-05-06 14:17:22 +100023 private BubblesThread thread = null;
24 private BubbleModel model;
alisionfe9cf712013-05-03 17:26:08 -040025
Adrien Béraud33268882013-05-18 03:41:15 +100026 private Paint attractor_paint = new Paint();
Adrien Béraud6bbce912013-05-24 00:48:13 +100027 private Paint name_paint = new Paint(Paint.ANTI_ALIAS_FLAG);
28
29 private float density;
30 private float textDensity;
Adrien Béraud33268882013-05-18 03:41:15 +100031
Adrien Béraud04463092013-05-06 14:17:22 +100032 public BubblesView(Context context, AttributeSet attrs)
33 {
34 super(context, attrs);
alisionfe9cf712013-05-03 17:26:08 -040035
Adrien Béraud6bbce912013-05-24 00:48:13 +100036 density = getResources().getDisplayMetrics().density;
37 textDensity = getResources().getDisplayMetrics().scaledDensity;
38
Adrien Béraud04463092013-05-06 14:17:22 +100039 SurfaceHolder holder = getHolder();
40 holder.addCallback(this);
alisionfe9cf712013-05-03 17:26:08 -040041
Adrien Béraud04463092013-05-06 14:17:22 +100042 // create thread only; it's started in surfaceCreated()
43 createThread();
alisionfe9cf712013-05-03 17:26:08 -040044
Adrien Béraud04463092013-05-06 14:17:22 +100045 setOnTouchListener(this);
46 setFocusable(true);
Adrien Béraud33268882013-05-18 03:41:15 +100047
48 attractor_paint.setColor(Color.RED);
49 //attractor_paint.set
Adrien Béraud6bbce912013-05-24 00:48:13 +100050 name_paint.setTextSize(20*textDensity);
51 name_paint.setColor(0xFF303030);
52 name_paint.setTextAlign(Align.CENTER);
Adrien Béraud04463092013-05-06 14:17:22 +100053 }
alisionfe9cf712013-05-03 17:26:08 -040054
Adrien Béraude0ef0c22013-05-18 01:56:27 +100055 private void createThread()
56 {
57 if (thread != null)
58 return;
Adrien Béraud04463092013-05-06 14:17:22 +100059 thread = new BubblesThread(getHolder(), getContext(), new Handler() {
60 @Override
61 public void handleMessage(Message m)
62 {
63 /* mStatusText.setVisibility(m.getData().getInt("viz"));
64 mStatusText.setText(m.getData().getString("text"));*/
65 }
66 });
Adrien Béraude0ef0c22013-05-18 01:56:27 +100067 if (model != null)
Adrien Béraud65ba5b12013-05-06 15:42:50 +100068 thread.setModel(model);
Adrien Béraud04463092013-05-06 14:17:22 +100069 }
alisionfe9cf712013-05-03 17:26:08 -040070
Adrien Béraud04463092013-05-06 14:17:22 +100071 public void setModel(BubbleModel model)
72 {
73 this.model = model;
74 thread.setModel(model);
75 }
alisionfe9cf712013-05-03 17:26:08 -040076
Adrien Béraud04463092013-05-06 14:17:22 +100077 /*@Override
78 public void onWindowFocusChanged(boolean hasWindowFocus) {
79 if (!hasWindowFocus) {
80 thread.pause();
81 }
82 }*/
alisionfe9cf712013-05-03 17:26:08 -040083
Adrien Béraud04463092013-05-06 14:17:22 +100084 @Override
85 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
86 {
87 Log.w(TAG, "surfaceChanged");
88 thread.setSurfaceSize(width, height);
89 }
alisionfe9cf712013-05-03 17:26:08 -040090
Adrien Béraud04463092013-05-06 14:17:22 +100091 /*
92 * Callback invoked when the Surface has been created and is ready to be
93 * used.
94 */
95 @Override
96 public void surfaceCreated(SurfaceHolder holder)
97 {
98 // start the thread here so that we don't busy-wait in run()
99 // waiting for the surface to be created
100 createThread();
alisionfe9cf712013-05-03 17:26:08 -0400101
Adrien Béraud04463092013-05-06 14:17:22 +1000102 Log.w(TAG, "surfaceCreated");
103 thread.setRunning(true);
104 thread.start();
105 }
alisionfe9cf712013-05-03 17:26:08 -0400106
Adrien Béraud04463092013-05-06 14:17:22 +1000107 /*
108 * Callback invoked when the Surface has been destroyed and must no longer
109 * be touched. WARNING: after this method returns, the Surface/Canvas must
110 * never be touched again!
111 */
112 @Override
113 public void surfaceDestroyed(SurfaceHolder holder)
114 {
115 // we have to tell thread to shut down & wait for it to finish, or else
116 // it might touch the Surface after we return and explode
117 Log.w(TAG, "surfaceDestroyed");
118 boolean retry = true;
119 thread.setRunning(false);
Adrien Béraud25fc4092013-05-06 15:28:39 +1000120 while (retry) {
Adrien Béraud04463092013-05-06 14:17:22 +1000121 try {
122 thread.join();
123 retry = false;
124 } catch (InterruptedException e) {
125 }
Adrien Béraud25fc4092013-05-06 15:28:39 +1000126 }
Adrien Béraud04463092013-05-06 14:17:22 +1000127 thread = null;
128 }
alisionfe9cf712013-05-03 17:26:08 -0400129
Adrien Béraud04463092013-05-06 14:17:22 +1000130 @Override
131 public boolean onTouch(View v, MotionEvent event)
132 {
133 Log.w(TAG, "onTouch " + event.getAction());
Adrien Béraud25fc4092013-05-06 15:28:39 +1000134
135 int action = event.getActionMasked();
136
Adrien Béraude0ef0c22013-05-18 01:56:27 +1000137 if (action == MotionEvent.ACTION_DOWN) {
Adrien Béraud04463092013-05-06 14:17:22 +1000138 for (Bubble b : model.listBubbles) {
Adrien Béraude0ef0c22013-05-18 01:56:27 +1000139 if (b.intersects(event.getX(), event.getY())) {
Adrien Béraud25fc4092013-05-06 15:28:39 +1000140 b.dragged = true;
Adrien Béraude0ef0c22013-05-18 01:56:27 +1000141 b.last_drag = System.nanoTime();
Adrien Béraud6bbce912013-05-24 00:48:13 +1000142 b.setPos(event.getX(), event.getY());
143 b.target_scale = .8f;
Adrien Béraud25fc4092013-05-06 15:28:39 +1000144 }
145 }
146 } else if (action == MotionEvent.ACTION_MOVE) {
Adrien Béraud6bbce912013-05-24 00:48:13 +1000147 long now = System.nanoTime();
Adrien Béraud25fc4092013-05-06 15:28:39 +1000148 for (Bubble b : model.listBubbles) {
Adrien Béraude0ef0c22013-05-18 01:56:27 +1000149 if (b.dragged) {
150 float x = event.getX(), y = event.getY();
Adrien Béraude0ef0c22013-05-18 01:56:27 +1000151 float dt = (float) ((now-b.last_drag)/1000000000.);
152 float dx = x - b.getPosX(), dy = y - b.getPosY();
153 b.last_drag = now;
154
Adrien Béraud25fc4092013-05-06 15:28:39 +1000155 b.setPos(event.getX(), event.getY());
Adrien Béraude0ef0c22013-05-18 01:56:27 +1000156 /*int hn = event.getHistorySize() - 2;
157 Log.w(TAG, "event.getHistorySize() : " + event.getHistorySize());
158 if(hn > 0) {
159 float dx = x-event.getHistoricalX(hn);
160 float dy = y-event.getHistoricalY(hn);
161 float dt = event.getHistoricalEventTime(hn)/1000.f;*/
162 b.speed.x = dx/dt;
163 b.speed.y = dy/dt;
164 //Log.w(TAG, "onTouch dx:" + b.speed.x + " dy:" + b.speed.y);
165 //}
Adrien Béraud04463092013-05-06 14:17:22 +1000166 return true;
167 }
168 }
Adrien Béraude0ef0c22013-05-18 01:56:27 +1000169 } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
Adrien Béraud25fc4092013-05-06 15:28:39 +1000170 for (Bubble b : model.listBubbles) {
Adrien Béraude0ef0c22013-05-18 01:56:27 +1000171 if (b.dragged) {
Adrien Béraud25fc4092013-05-06 15:28:39 +1000172 b.dragged = false;
Adrien Béraud6bbce912013-05-24 00:48:13 +1000173 b.target_scale = 1.f;
Adrien Béraud25fc4092013-05-06 15:28:39 +1000174 }
175 }
176 }
Adrien Béraud04463092013-05-06 14:17:22 +1000177 return true;
178 }
alisionfe9cf712013-05-03 17:26:08 -0400179
Adrien Béraud04463092013-05-06 14:17:22 +1000180 class BubblesThread extends Thread
181 {
182 private boolean running = false;
183 private SurfaceHolder surfaceHolder;
alisionfe9cf712013-05-03 17:26:08 -0400184
Adrien Béraud04463092013-05-06 14:17:22 +1000185 BubbleModel model = null;
alisionfe9cf712013-05-03 17:26:08 -0400186
Adrien Béraud04463092013-05-06 14:17:22 +1000187 public BubblesThread(SurfaceHolder holder, Context context, Handler handler)
188 {
189 surfaceHolder = holder;
190 }
alisionfe9cf712013-05-03 17:26:08 -0400191
Adrien Béraud04463092013-05-06 14:17:22 +1000192 public void setModel(BubbleModel model)
193 {
194 this.model = model;
195 }
alisionfe9cf712013-05-03 17:26:08 -0400196
Adrien Béraud04463092013-05-06 14:17:22 +1000197 @Override
198 public void run()
199 {
200 while (running) {
201 Canvas c = null;
202 try {
203 c = surfaceHolder.lockCanvas(null);
Adrien Béraud7ed23dc2013-05-06 16:27:24 +1000204
205 // for the case the surface is destroyed while already in the loop
Adrien Béraude0ef0c22013-05-18 01:56:27 +1000206 if (c == null || model == null)
207 continue;
Adrien Béraud7ed23dc2013-05-06 16:27:24 +1000208
Adrien Béraud04463092013-05-06 14:17:22 +1000209 synchronized (surfaceHolder) {
Adrien Béraud25fc4092013-05-06 15:28:39 +1000210 //Log.w(TAG, "Thread doDraw");
211 model.update();
Adrien Béraud04463092013-05-06 14:17:22 +1000212 doDraw(c);
213 }
214 } finally {
215 // do this in a finally so that if an exception is thrown
216 // during the above, we don't leave the Surface in an
217 // inconsistent state
218 if (c != null)
219 surfaceHolder.unlockCanvasAndPost(c);
220 }
221 }
222 }
223
224 public void setRunning(boolean b)
225 {
226 running = b;
227 }
228
229 public void setSurfaceSize(int width, int height)
230 {
231 synchronized (surfaceHolder) {
Adrien Béraude0ef0c22013-05-18 01:56:27 +1000232 if (model != null) {
Adrien Béraud25fc4092013-05-06 15:28:39 +1000233 model.width = width;
234 model.height = height;
235 }
Adrien Béraud04463092013-05-06 14:17:22 +1000236
237 // don't forget to resize the background image
238 // mBackgroundImage = Bitmap.createScaledBitmap(mBackgroundImage, width, height, true);
239 }
240 }
241
Adrien Béraud04463092013-05-06 14:17:22 +1000242 private void doDraw(Canvas canvas)
243 {
244 canvas.drawColor(Color.WHITE);
245
Adrien Béraude0ef0c22013-05-18 01:56:27 +1000246 synchronized (model) {
Adrien Béraud33268882013-05-18 03:41:15 +1000247 for (int i = 0; i < model.attractors.size(); i++) {
248 Attractor a = model.attractors.get(i);
249 canvas.drawCircle(a.pos.x, a.pos.y, 10, attractor_paint);
250 }
251
Adrien Béraude0ef0c22013-05-18 01:56:27 +1000252 for (int i = 0; i < model.listBubbles.size(); i++) {
253 Bubble b = model.listBubbles.get(i);
Adrien Béraud6bbce912013-05-24 00:48:13 +1000254 RectF bounds = new RectF(b.getBounds());
255 /*if(b.dragged) {
256 float width = bounds.left - bounds.right;
257 float red = width/4;
258 bounds.left += red;
259 bounds.right -= red;
260 bounds.top += red;
261 bounds.bottom -= red;
262 }*/
263 canvas.drawBitmap(b.getBitmap(), null, bounds, null);
264 canvas.drawText(b.contact.getmDisplayName(), b.getPosX(), b.getPosY()-50*density, name_paint);
Adrien Béraude0ef0c22013-05-18 01:56:27 +1000265 }
Adrien Béraud25fc4092013-05-06 15:28:39 +1000266 }
Adrien Béraud04463092013-05-06 14:17:22 +1000267 }
268 }
269
alisionfe9cf712013-05-03 17:26:08 -0400270}