Skip to content
Snippets Groups Projects
Commit 6bbce918 authored by Adrien Béraud's avatar Adrien Béraud
Browse files

Bubble improvements

parent 7297bdbd
No related branches found
No related tags found
No related merge requests found
package com.savoirfairelinux.sflphone.client;
import android.app.Activity;
import android.graphics.PointF;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import com.savoirfairelinux.sflphone.R;
import com.savoirfairelinux.sflphone.model.Bubble;
import com.savoirfairelinux.sflphone.model.BubbleModel;
import com.savoirfairelinux.sflphone.model.BubblesView;
public class BubblesViewActivity extends Activity {
private static final String TAG = BubblesViewActivity.class.getSimpleName();
BubblesView view;
PointF screenCenter;
int radiusCalls;
double angle_part;
BubbleModel model;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bubbleview_layout);
model = new BubbleModel();
DisplayMetrics metrics = getResources().getDisplayMetrics();
screenCenter = new PointF(metrics.widthPixels / 2, metrics.heightPixels / 3);
radiusCalls = metrics.widthPixels / 2 - 150;
// model.listBubbles.add(new Bubble(this, metrics.widthPixels / 2, metrics.heightPixels / 4, 150, R.drawable.me));
// model.listBubbles.add(new Bubble(this, metrics.widthPixels / 2, metrics.heightPixels / 4 * 3, 150, R.drawable.callee));
view = (BubblesView) findViewById(R.id.main_view);
view.setModel(model);
/*
((Button) findViewById(R.id.add_bubble)).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
addBubble();
}
});
((Button) findViewById(R.id.remove_bubble)).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
removeBubble();
}
});
*/
}
public void addBubble() {
/*
* Bubble.Builder builder = new Bubble.Builder(getContext()); builder.setRadiusPixels(200).setX(200).setY(300);
*/
DisplayMetrics metrics = getResources().getDisplayMetrics();
Bubble b = new Bubble(this, metrics.widthPixels / 3, metrics.heightPixels / 4 * 3, 150, -1);
model.listBubbles.add(b);
angle_part = 2*Math.PI / model.listBubbles.size();
double dX = 0;
double dY = 0;
for (int i = 0; i < model.listBubbles.size(); ++i) {
dX = Math.cos(angle_part * i) * radiusCalls;
dY = Math.sin(angle_part * i) * radiusCalls;
Log.i(TAG, "dX " + dX + " dY " + dY);
model.listBubbles.get(i).setAttractor(new PointF((int) dX + screenCenter.x, (int) dY + screenCenter.y));
}
// listBubbles.get(listBubbles.size() - 1).setRegion(width, height);
}
public void removeBubble() {
if (model.listBubbles.isEmpty()) {
return;
}
/*
* Bubble.Builder builder = new Bubble.Builder(getContext()); builder.setRadiusPixels(200).setX(200).setY(300);
*/
// DisplayMetrics metrics = getResources().getDisplayMetrics();
// Bubble b = new Bubble(this, metrics.widthPixels / 3, metrics.heightPixels / 4 * 3, 150, -1);
synchronized (model) {
model.listBubbles.remove(model.listBubbles.size() - 1);
}
if (model.listBubbles.isEmpty()) {
return;
}
angle_part = 2*Math.PI / model.listBubbles.size();
Log.i(TAG, "Angle:" + angle_part);
double dX = 0;
double dY = 0;
for (int i = 0; i < model.listBubbles.size(); ++i) {
dX = Math.cos(angle_part * i) * radiusCalls;
dY = Math.sin(angle_part * i) * radiusCalls;
Log.i(TAG, "dX " + dX + " dY " + dY);
model.listBubbles.get(i).setAttractor(new PointF((int) dX + screenCenter.x, (int) dY + screenCenter.y));
}
// listBubbles.get(listBubbles.size() - 1).setRegion(width, height);
}
}
\ No newline at end of file
......@@ -42,7 +42,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.drm.DrmStore.Action;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.os.Bundle;
......@@ -60,17 +59,16 @@ import com.savoirfairelinux.sflphone.model.Bubble;
import com.savoirfairelinux.sflphone.model.BubbleModel;
import com.savoirfairelinux.sflphone.model.BubblesView;
import com.savoirfairelinux.sflphone.model.CallContact;
import com.savoirfairelinux.sflphone.model.CallContact.Phone;
import com.savoirfairelinux.sflphone.model.SipCall;
import com.savoirfairelinux.sflphone.service.ISipClient;
import com.savoirfairelinux.sflphone.service.ISipService;
import com.savoirfairelinux.sflphone.service.SipService;
public class CallActivity extends Activity //implements IncomingCallFragment.ICallActionListener, OngoingCallFragment.ICallActionListener //OnClickListener
public class CallActivity extends Activity
{
static final String TAG = "CallActivity";
private ISipService service;
private String pendingAction;
private String pendingAction = null;
private SipCall mCall;
private BubblesView view;
......@@ -78,7 +76,7 @@ public class CallActivity extends Activity //implements IncomingCallFragment.ICa
private PointF screenCenter;
private DisplayMetrics metrics;
private HashMap<Bubble, CallContact> contacts = new HashMap<Bubble, CallContact>();
private HashMap<CallContact, Bubble> contacts = new HashMap<CallContact, Bubble>();
private ExecutorService infos_fetcher = Executors.newCachedThreadPool();
......@@ -137,7 +135,7 @@ public class CallActivity extends Activity //implements IncomingCallFragment.ICa
super.onCreate(savedInstanceState);
setContentView(R.layout.bubbleview_layout);
model = new BubbleModel();
model = new BubbleModel(getResources().getDisplayMetrics().density);
metrics = getResources().getDisplayMetrics();
screenCenter = new PointF(metrics.widthPixels / 2, metrics.heightPixels / 3);
//radiusCalls = metrics.widthPixels / 2 - 150;
......@@ -155,30 +153,30 @@ public class CallActivity extends Activity //implements IncomingCallFragment.ICa
// mCall = new SipCall(info);
//
Intent intent = new Intent(this, SipService.class);
//setCallStateDisplay(mCall.getCallStateString());
pendingAction = b.getString("action");
if(pendingAction.equals("call")) {
if(pendingAction != null && pendingAction.equals("call")) {
CallContact contact = b.getParcelable("CallContact");
Log.i(TAG,"Calling "+ contact.getmDisplayName());
callContact(contact);
// SipCall.CallInfo info = new SipCall.CallInfo();
// Random random = new Random();
// String callID = Integer.toString(random.nextInt());
// Phone phone = contact.getSipPhone();
// info.mCallID = callID;
// info.mAccountID = ""+contact.getId();
// info.mDisplayName = contact.getmDisplayName();
// info.mPhone = phone==null?null:phone.toString();
// info.mEmail = contact.getmEmail();
// info.mCallType = SipCall.CALL_TYPE_OUTGOING;
// mCall = CallListReceiver.getCallInstance(info);
// SipCall.CallInfo info = new SipCall.CallInfo();
// Random random = new Random();
// String callID = Integer.toString(random.nextInt());
// Phone phone = contact.getSipPhone();
// info.mCallID = callID;
// info.mAccountID = ""+contact.getId();
// info.mDisplayName = contact.getmDisplayName();
// info.mPhone = phone==null?null:phone.toString();
// info.mEmail = contact.getmEmail();
// info.mCallType = SipCall.CALL_TYPE_OUTGOING;
// mCall = CallListReceiver.getCallInstance(info);
//mCallbacks.onCallSelected(call);
/* try {
......@@ -187,9 +185,9 @@ public class CallActivity extends Activity //implements IncomingCallFragment.ICa
Log.e(TAG, "Cannot call service method", e);
}*/
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
} else if(pendingAction.equals("incoming")) {
} else if(pendingAction.equals("incoming")) {
callIncoming();
}
/*
......@@ -219,8 +217,9 @@ public class CallActivity extends Activity //implements IncomingCallFragment.ICa
}
}));
contact_bubble.contact = contact;
model.listBubbles.add(contact_bubble);
contacts.put(contact_bubble, contact);
contacts.put(contact, contact_bubble);
}
private void callIncoming() {
......@@ -259,22 +258,24 @@ public class CallActivity extends Activity //implements IncomingCallFragment.ICa
service = ISipService.Stub.asInterface(binder);
try {
service.registerClient(callback);
if(pendingAction.contentEquals("call")){
Log.i(TAG, "Placing call");
Random random = new Random();
String callID = Integer.toString(random.nextInt());
SipCall.CallInfo info = new SipCall.CallInfo();
info.mCallID = callID;
info.mAccountID = service.getAccountList().get(1).toString();
info.mDisplayName = "Cool Guy!";
info.mPhone = contacts.get(contacts.keySet().iterator().next()).getPhones().get(0).getNumber();
info.mEmail = "coolGuy@coolGuy.com";
info.mCallType = SipCall.CALL_TYPE_OUTGOING;
mCall = CallListReceiver.getCallInstance(info);
service.placeCall(info.mAccountID, info.mCallID, info.mPhone);
if(pendingAction != null && pendingAction.contentEquals("call")){
Log.i(TAG, "Placing call");
CallContact contact = model.listBubbles.get(0).contact;
String callID = Integer.toString(new Random().nextInt());
SipCall.CallInfo info = new SipCall.CallInfo();
info.mCallID = callID;
info.mAccountID = service.getAccountList().get(0).toString();
info.mDisplayName = contact.getmDisplayName();
info.mPhone = contact.getSipPhone().getNumber();
info.mEmail = contact.getmEmail();
info.mCallType = SipCall.CALL_TYPE_OUTGOING;
mCall = CallListReceiver.getCallInstance(info);
service.placeCall(info.mAccountID, info.mCallID, info.mPhone);
pendingAction = null;
}
} catch (RemoteException e) {
Log.e(TAG, e.toString());
......
......@@ -15,12 +15,15 @@ import com.savoirfairelinux.sflphone.R;
public class Bubble
{
public CallContact contact;
// A Bitmap object that is going to be passed to the BitmapShader
private Bitmap internalBMP, externalBMP;
private PointF pos = new PointF();
private RectF bounds;
private float radius;
public float target_scale = 1.f;
private final float radius;
private float scale = 1.f;
public PointF speed = new PointF(0, 0);
public PointF last_speed = new PointF();
public PointF attractor = null;
......@@ -85,6 +88,17 @@ public class Bubble
return bounds;
}
public void set(float x, float y, float s) {
scale = s;
pos.x = x;
pos.y = y;
float rad = scale*radius;
bounds.left = pos.x - rad;
bounds.right = pos.x + rad;
bounds.top = pos.y - rad;
bounds.bottom = pos.y + rad;
}
public float getPosX() {
return pos.x;
}
......@@ -94,16 +108,7 @@ public class Bubble
}
public void setPos(float x, float y) {
pos.x = x;
pos.y = y;
bounds.left = pos.x - radius;
bounds.right = pos.x + radius;
bounds.top = pos.y - radius;
bounds.bottom = pos.y + radius;
}
public float getRadius() {
return radius;
set(x, y, scale);
}
public PointF getPos()
......@@ -111,6 +116,18 @@ public class Bubble
return pos;
}
public float getScale() {
return scale;
}
public void setScale(float s) {
set(pos.x, pos.y, s);
}
public float getRadius() {
return radius;
}
/**
* Point intersection test.
*/
......
......@@ -3,7 +3,6 @@ package com.savoirfairelinux.sflphone.model;
import java.util.ArrayList;
import android.graphics.PointF;
import android.util.Log;
public class BubbleModel
{
......@@ -14,18 +13,35 @@ public class BubbleModel
public ArrayList<Bubble> listBubbles = new ArrayList<Bubble>();
public ArrayList<Attractor> attractors = new ArrayList<Attractor>();
private static final float ATTRACTOR_DIST_SUCK = 20.f;
private static final double BUBBLE_RETURN_TIME_HALF_LIFE = .25;
private static final double BUBBLE_RETURN_TIME_HALF_LIFE = .3;
private static final double BUBBLE_RETURN_TIME_LAMBDA = Math.log(2)/BUBBLE_RETURN_TIME_HALF_LIFE;
/*private static final float FRICTION_VISCOUS = .5f; // Viscous friction factor
private static final double FRICTION_VISCOUS = Math.log(2)/.2f; // Viscous friction factor
private static final float BUBBLE_MAX_SPEED = 2500.f; // px.s-1 : Max target speed in px/sec
private static final float ATTRACTOR_SMOOTH_DIST = 50.f; // px : Size of the "gravity hole" around the attractor
private static final float ATTRACTOR_STALL_DIST = 15.f; // px : Size of the "gravity hole" flat bottom
private static final float ATTRACTOR_DIST_SUCK = 20.f; // px
private static final float BORDER_REPULSION = 60000; // px.s^-2
private final float border_repulsion;
private final float bubble_max_speed;
private final float attractor_smooth_dist;
private final float attractor_stall_dist;
private final float attractor_dist_suck;
private float density = 1.f;
public BubbleModel(float screen_density) {
this.density = screen_density;
attractor_dist_suck = ATTRACTOR_DIST_SUCK*density;
bubble_max_speed = BUBBLE_MAX_SPEED*density;
attractor_smooth_dist = ATTRACTOR_SMOOTH_DIST*density;
attractor_stall_dist = ATTRACTOR_STALL_DIST*density;
border_repulsion = BORDER_REPULSION*density;
}
private static final float BUBBLE_MAX_SPEED = 2500.f; // Max target speed in px/sec
private static final float ATTRACTOR_SMOOTH_DIST = 100.f;// Size of the "gravity hole" around the attractor
private static final float ATTRACTOR_STALL_DIST = 15.f; // Size of the "gravity hole" flat bottom
private static final float ATTRACTOR_ACCEL = 10.f; // Acceleration factor towards target speed
*/
public void update()
{
long now = System.nanoTime();
......@@ -65,76 +81,66 @@ public class BubbleModel
}
}
double edt = -Math.expm1(-BUBBLE_RETURN_TIME_LAMBDA*dt);
double dx = (attractor_pos.x - bx) * edt;
double dy = (attractor_pos.y - by) * edt;
// Log.w(TAG, "update dx="+dt+" dy="+dy);
b.setPos((float)(bx+dx), (float)(by+dy));
if(attractor != null && attractor_dist < ATTRACTOR_DIST_SUCK*ATTRACTOR_DIST_SUCK) {
attractor.callback.onBubbleSucked(b);
listBubbles.remove(b);
n--;
}
/* float bx=b.getPosX(), by=b.getPosY();
/// Apply viscous friction
float friction_coef = 1.f-FRICTION_VISCOUS*dt;
float tdx = b.attractor.x - bx, tdy = b.attractor.y - by;
float dist = (float) Math.sqrt(tdx*tdx + tdy*tdy);
float speed = (float)Math.sqrt(b.speed.x*b.speed.x + b.speed.y*b.speed.y);
//float friction_coef = 1.f-FRICTION_VISCOUS*dt;
double friction_coef = 1+Math.expm1(-FRICTION_VISCOUS*ddt);
b.speed.x *= friction_coef;
b.speed.y *= friction_coef;
//if(attractor != null) {
float target_speed;
float tdx = attractor_pos.x - bx, tdy = attractor_pos.y - by;
float dist = Math.max(1.f, (float) Math.sqrt(tdx*tdx + tdy*tdy));
if(dist > attractor_smooth_dist)
target_speed = bubble_max_speed;
else if(dist < attractor_stall_dist)
target_speed = 0;
else {
float a = (dist-attractor_stall_dist)/(attractor_smooth_dist-attractor_stall_dist);
target_speed = bubble_max_speed*a;
}
if(attractor != null) {
if(dist > attractor_smooth_dist)
b.target_scale = 1.f;
else if(dist < attractor_stall_dist)
b.target_scale = .2f;
else {
float a = (dist-attractor_stall_dist)/(attractor_smooth_dist-attractor_stall_dist);
b.target_scale = a*.8f+.2f;
}
if(speed > 10.f || dist > ATTRACTOR_STALL_DIST) {
dist = Math.max(1.f, dist); // Avoid division by 0
b.speed.x *= friction_coef;
b.speed.y *= friction_coef;
// Target speed (defines the "gravity hole")
float target_speed;
if(dist > ATTRACTOR_SMOOTH_DIST)
target_speed = BUBBLE_MAX_SPEED;
else if(dist < ATTRACTOR_STALL_DIST)
target_speed = 0;
else
target_speed = BUBBLE_MAX_SPEED/(ATTRACTOR_SMOOTH_DIST-ATTRACTOR_STALL_DIST)*(dist-ATTRACTOR_STALL_DIST);
float target_speed_x = target_speed*tdx/dist;
float target_speed_y = target_speed*tdy/dist;
// Acceleration
float ax = (target_speed_x-b.speed.x) * ATTRACTOR_ACCEL;// + 2*(b.last_speed.x-b.speed.x)*(1-FRICTION_VISCOUS)/FRICTION_VISCOUS*60.f;
float ay = (target_speed_y-b.speed.y) * ATTRACTOR_ACCEL;// + 2*(b.last_speed.y-b.speed.y)*(1-FRICTION_VISCOUS)/FRICTION_VISCOUS*60.f;
// Speed update
b.speed.x += ax*dt;
b.speed.y += ay*dt;
b.last_speed.set(b.speed);
Log.w(TAG, "dist " + dist + " speed " + Math.sqrt(b.speed.x*b.speed.x + b.speed.y*b.speed.y) + " target speed "+target_speed);
// Position update
float dx = b.speed.x * dt;
float dy = b.speed.y * dt;
b.setPos(bx+dx, by+dy);
}
// border repulsion
if(bx < 0 && b.speed.x < 0) {
b.speed.x += dt * border_repulsion;
} else if(bx > width && b.speed.x > 0) {
b.speed.x -= dt * border_repulsion;
}
if(by < 0 && b.speed.y < 0) {
b.speed.y += dt * border_repulsion;
} else if(by > height && b.speed.y > 0) {
b.speed.y -= dt * border_repulsion;
}
// Prevent speed higher than BUBBLE_MAX_SPEED
float ds = (target_speed-speed)*dt;
// Set motion direction and speed
float nsr = (speed>BUBBLE_MAX_SPEED ? BUBBLE_MAX_SPEED : speed+ds)/(dist < 1.f ? 1.f : dist);
b.speed.x = tdx * nsr;
b.speed.y = tdy * nsr;*/
b.speed.x += dt * target_speed * tdx/dist;
b.speed.y += dt * target_speed * tdy/dist;
double edt = -Math.expm1(-BUBBLE_RETURN_TIME_LAMBDA*ddt);
double dx = (attractor_pos.x - bx) * edt + Math.min(bubble_max_speed, b.speed.x) * dt;
double dy = (attractor_pos.y - by) * edt + Math.min(bubble_max_speed, b.speed.y) * dt;
// Log.w(TAG, "update dx="+dt+" dy="+dy);
b.setPos((float)(bx+dx), (float)(by+dy));
if(attractor != null && attractor_dist < attractor_dist_suck*attractor_dist_suck) {
attractor.callback.onBubbleSucked(b);
listBubbles.remove(b);
n--;
}
}
b.setScale(b.getScale() + (b.target_scale-b.getScale())*dt*10.f);
}
}
}
......@@ -4,6 +4,8 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
......@@ -22,11 +24,18 @@ public class BubblesView extends SurfaceView implements SurfaceHolder.Callback,
private BubbleModel model;
private Paint attractor_paint = new Paint();
private Paint name_paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private float density;
private float textDensity;
public BubblesView(Context context, AttributeSet attrs)
{
super(context, attrs);
density = getResources().getDisplayMetrics().density;
textDensity = getResources().getDisplayMetrics().scaledDensity;
SurfaceHolder holder = getHolder();
holder.addCallback(this);
......@@ -38,6 +47,9 @@ public class BubblesView extends SurfaceView implements SurfaceHolder.Callback,
attractor_paint.setColor(Color.RED);
//attractor_paint.set
name_paint.setTextSize(20*textDensity);
name_paint.setColor(0xFF303030);
name_paint.setTextAlign(Align.CENTER);
}
private void createThread()
......@@ -127,13 +139,15 @@ public class BubblesView extends SurfaceView implements SurfaceHolder.Callback,
if (b.intersects(event.getX(), event.getY())) {
b.dragged = true;
b.last_drag = System.nanoTime();
b.setPos(event.getX(), event.getY());
b.target_scale = .8f;
}
}
} else if (action == MotionEvent.ACTION_MOVE) {
long now = System.nanoTime();
for (Bubble b : model.listBubbles) {
if (b.dragged) {
float x = event.getX(), y = event.getY();
long now = System.nanoTime();
float dt = (float) ((now-b.last_drag)/1000000000.);
float dx = x - b.getPosX(), dy = y - b.getPosY();
b.last_drag = now;
......@@ -156,6 +170,7 @@ public class BubblesView extends SurfaceView implements SurfaceHolder.Callback,
for (Bubble b : model.listBubbles) {
if (b.dragged) {
b.dragged = false;
b.target_scale = 1.f;
}
}
}
......@@ -236,7 +251,17 @@ public class BubblesView extends SurfaceView implements SurfaceHolder.Callback,
for (int i = 0; i < model.listBubbles.size(); i++) {
Bubble b = model.listBubbles.get(i);
canvas.drawBitmap(b.getBitmap(), null, b.getBounds(), null);
RectF bounds = new RectF(b.getBounds());
/*if(b.dragged) {
float width = bounds.left - bounds.right;
float red = width/4;
bounds.left += red;
bounds.right -= red;
bounds.top += red;
bounds.bottom -= red;
}*/
canvas.drawBitmap(b.getBitmap(), null, bounds, null);
canvas.drawText(b.contact.getmDisplayName(), b.getPosX(), b.getPosY()-50*density, name_paint);
}
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment