Bubble improvements
diff --git a/src/com/savoirfairelinux/sflphone/client/BubblesViewActivity.java b/src/com/savoirfairelinux/sflphone/client/BubblesViewActivity.java
deleted file mode 100644
index d65b0d8..0000000
--- a/src/com/savoirfairelinux/sflphone/client/BubblesViewActivity.java
+++ /dev/null
@@ -1,111 +0,0 @@
-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
diff --git a/src/com/savoirfairelinux/sflphone/client/CallActivity.java b/src/com/savoirfairelinux/sflphone/client/CallActivity.java
index df8c27f..fd2f038 100644
--- a/src/com/savoirfairelinux/sflphone/client/CallActivity.java
+++ b/src/com/savoirfairelinux/sflphone/client/CallActivity.java
@@ -42,7 +42,6 @@
 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.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 @@
 	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 @@
 		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 @@
 		//		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();
+			//			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;
+			//			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);
-			
-			
+			//			mCall = CallListReceiver.getCallInstance(info);
+
+
 			//mCallbacks.onCallSelected(call);
 
 			/*	try {
@@ -187,9 +185,9 @@
 				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 @@
 			}
 		}));
 
+		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 @@
 			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);
+				if(pendingAction != null && pendingAction.contentEquals("call")){
 
-			        service.placeCall(info.mAccountID, info.mCallID, info.mPhone);
+					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());
diff --git a/src/com/savoirfairelinux/sflphone/fragments/CallElementListFragment.java b/src/com/savoirfairelinux/sflphone/fragments/CallElementListFragment.java
index fcf4eda..a8d28ab 100644
--- a/src/com/savoirfairelinux/sflphone/fragments/CallElementListFragment.java
+++ b/src/com/savoirfairelinux/sflphone/fragments/CallElementListFragment.java
@@ -72,7 +72,6 @@
 import com.savoirfairelinux.sflphone.R;
 import com.savoirfairelinux.sflphone.account.AccountSelectionSpinner;
 import com.savoirfairelinux.sflphone.adapters.CallElementAdapter;
-import com.savoirfairelinux.sflphone.client.BubblesViewActivity;
 import com.savoirfairelinux.sflphone.client.CallActivity;
 import com.savoirfairelinux.sflphone.client.SFLPhoneHomeActivity;
 import com.savoirfairelinux.sflphone.client.SFLPhonePreferenceActivity;
@@ -85,393 +84,393 @@
  * Main list of Call Elements. We don't manage contacts ourself so they are
  */
 public class CallElementListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
-    private static final String TAG = CallElementListFragment.class.getSimpleName();
-    private CallElementAdapter mAdapter;
-    private String mCurFilter;
-    private SFLPhoneHomeActivity sflphoneHome;
-    private ISipService service;
-    ImageButton buttonCall;
-    Button attendedTransfer, conference;
-    EditText phoneField;
-    private AccountSelectionSpinner mAccountSelectionSpinner;
-    // private AccountListReceiver mAccountList;
-    private boolean isReady = false;
+	private static final String TAG = CallElementListFragment.class.getSimpleName();
+	private CallElementAdapter mAdapter;
+	private String mCurFilter;
+	private SFLPhoneHomeActivity sflphoneHome;
+	private ISipService service;
+	ImageButton buttonCall;
+	Button attendedTransfer, conference;
+	EditText phoneField;
+	private AccountSelectionSpinner mAccountSelectionSpinner;
+	// private AccountListReceiver mAccountList;
+	private boolean isReady = false;
 
-    static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.PHOTO_ID, Contacts.LOOKUP_KEY };
-    static final String[] CONTACTS_PHONES_PROJECTION = new String[] { Phone.NUMBER, Phone.TYPE };
-    static final String[] CONTACTS_SIP_PROJECTION = new String[] { SipAddress.SIP_ADDRESS, SipAddress.TYPE };
+	static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.PHOTO_ID, Contacts.LOOKUP_KEY };
+	static final String[] CONTACTS_PHONES_PROJECTION = new String[] { Phone.NUMBER, Phone.TYPE };
+	static final String[] CONTACTS_SIP_PROJECTION = new String[] { SipAddress.SIP_ADDRESS, SipAddress.TYPE };
 
-    private Callbacks mCallbacks = sDummyCallbacks;
-    private ToggleButton switchHold;
-    /**
-     * A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity.
-     */
-    private static Callbacks sDummyCallbacks = new Callbacks() {
-        @Override
-        public void onCallSelected(SipCall c) {
-        }
+	private Callbacks mCallbacks = sDummyCallbacks;
+	private ToggleButton switchHold;
+	/**
+	 * A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity.
+	 */
+	private static Callbacks sDummyCallbacks = new Callbacks() {
+		@Override
+		public void onCallSelected(SipCall c) {
+		}
 
-        @Override
-        public ISipService getService() {
-            // TODO Auto-generated method stub
-            return null;
-        }
-    };
+		@Override
+		public ISipService getService() {
+			// TODO Auto-generated method stub
+			return null;
+		}
+	};
 
-    /**
-     * The Activity calling this fragment has to implement this interface
-     * 
-     */
-    public interface Callbacks {
-        public void onCallSelected(SipCall c);
+	/**
+	 * The Activity calling this fragment has to implement this interface
+	 * 
+	 */
+	public interface Callbacks {
+		public void onCallSelected(SipCall c);
 
-        public ISipService getService();
+		public ISipService getService();
 
-    }
+	}
 
-    @Override
-    public void onAttach(Activity activity) {
-        super.onAttach(activity);
-        sflphoneHome = (SFLPhoneHomeActivity) activity;
-        service = ((SFLphoneApplication) sflphoneHome.getApplication()).getSipService();
-        if (!(activity instanceof Callbacks)) {
-            throw new IllegalStateException("Activity must implement fragment's callbacks.");
-        }
+	@Override
+	public void onAttach(Activity activity) {
+		super.onAttach(activity);
+		sflphoneHome = (SFLPhoneHomeActivity) activity;
+		service = ((SFLphoneApplication) sflphoneHome.getApplication()).getSipService();
+		if (!(activity instanceof Callbacks)) {
+			throw new IllegalStateException("Activity must implement fragment's callbacks.");
+		}
 
-        mCallbacks = (Callbacks) activity;
-    }
+		mCallbacks = (Callbacks) activity;
+	}
 
-    @Override
-    public void onDetach() {
-        super.onDetach();
-        mCallbacks = sDummyCallbacks;
-    }
+	@Override
+	public void onDetach() {
+		super.onDetach();
+		mCallbacks = sDummyCallbacks;
+	}
 
-    public String getSelectedAccount() {
-        return mAccountSelectionSpinner.getAccount();
-    }
+	public String getSelectedAccount() {
+		return mAccountSelectionSpinner.getAccount();
+	}
 
-    /**
-     * Runnable that fill information in a contact card asynchroniously.
-     */
-    /*
-     * public static class InfosLoader implements Runnable { private View view; private long cid; private ContentResolver cr;
-     * 
-     * public InfosLoader(Context context, View element, long contact_id) { cid = contact_id; cr = context.getContentResolver(); view = element; }
-     * 
-     * public static Bitmap loadContactPhoto(ContentResolver cr, long id) { Uri uri =
-     * ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id); InputStream input =
-     * ContactsContract.Contacts.openContactPhotoInputStream(cr, uri); if (input == null) { return null; } return BitmapFactory.decodeStream(input); }
-     * 
-     * @Override public void run() { final Bitmap photo_bmp = loadContactPhoto(cr, cid);
-     * 
-     * Cursor phones = cr.query(CommonDataKinds.Phone.CONTENT_URI, CONTACTS_PHONES_PROJECTION, CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[]
-     * { Long.toString(cid) }, null);
-     * 
-     * final List<String> numbers = new ArrayList<String>(); while (phones.moveToNext()) { String number =
-     * phones.getString(phones.getColumnIndex(CommonDataKinds.Phone.NUMBER)); // int type =
-     * phones.getInt(phones.getColumnIndex(CommonDataKinds.Phone.TYPE)); numbers.add(number); } phones.close(); // TODO: same for SIP adresses.
-     * 
-     * final Bitmap bmp = photo_bmp; view.post(new Runnable() {
-     * 
-     * @Override public void run() { } }); } }
-     */
+	/**
+	 * Runnable that fill information in a contact card asynchroniously.
+	 */
+	/*
+	 * public static class InfosLoader implements Runnable { private View view; private long cid; private ContentResolver cr;
+	 * 
+	 * public InfosLoader(Context context, View element, long contact_id) { cid = contact_id; cr = context.getContentResolver(); view = element; }
+	 * 
+	 * public static Bitmap loadContactPhoto(ContentResolver cr, long id) { Uri uri =
+	 * ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id); InputStream input =
+	 * ContactsContract.Contacts.openContactPhotoInputStream(cr, uri); if (input == null) { return null; } return BitmapFactory.decodeStream(input); }
+	 * 
+	 * @Override public void run() { final Bitmap photo_bmp = loadContactPhoto(cr, cid);
+	 * 
+	 * Cursor phones = cr.query(CommonDataKinds.Phone.CONTENT_URI, CONTACTS_PHONES_PROJECTION, CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[]
+	 * { Long.toString(cid) }, null);
+	 * 
+	 * final List<String> numbers = new ArrayList<String>(); while (phones.moveToNext()) { String number =
+	 * phones.getString(phones.getColumnIndex(CommonDataKinds.Phone.NUMBER)); // int type =
+	 * phones.getInt(phones.getColumnIndex(CommonDataKinds.Phone.TYPE)); numbers.add(number); } phones.close(); // TODO: same for SIP adresses.
+	 * 
+	 * final Bitmap bmp = photo_bmp; view.post(new Runnable() {
+	 * 
+	 * @Override public void run() { } }); } }
+	 */
 
-    public CallElementListFragment() {
-        super();
-    }
+	public CallElementListFragment() {
+		super();
+	}
 
-    public void addCall(SipCall c) {
-        Log.i(TAG, "Adding call " + c.mCallInfo.mDisplayName);
-        mAdapter.add(c);
-    }
+	public void addCall(SipCall c) {
+		Log.i(TAG, "Adding call " + c.mCallInfo.mDisplayName);
+		mAdapter.add(c);
+	}
 
-    // public void removeCall(SipCall c) {
-    // Log.i(TAG, "Removing call " + c.mCallInfo.mDisplayName);
-    // mAdapter.remove(c);
-    // }
+	// public void removeCall(SipCall c) {
+	// Log.i(TAG, "Removing call " + c.mCallInfo.mDisplayName);
+	// mAdapter.remove(c);
+	// }
 
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
+	@Override
+	public void onActivityCreated(Bundle savedInstanceState) {
+		super.onActivityCreated(savedInstanceState);
 
-        // Give some text to display if there is no data. In a real
-        // application this would come from a resource.
-        // setEmptyText("No phone numbers");
+		// Give some text to display if there is no data. In a real
+		// application this would come from a resource.
+		// setEmptyText("No phone numbers");
 
-        // We have a menu item to show in action bar.
-        setHasOptionsMenu(true);
+		// We have a menu item to show in action bar.
+		setHasOptionsMenu(true);
 
-        // Create an empty adapter we will use to display the loaded data.
-        ArrayList<SipCall> calls = new ArrayList<SipCall>();
-        mAdapter = new CallElementAdapter(getActivity(), calls);
-        setListAdapter(mAdapter);
+		// Create an empty adapter we will use to display the loaded data.
+		ArrayList<SipCall> calls = new ArrayList<SipCall>();
+		mAdapter = new CallElementAdapter(getActivity(), calls);
+		setListAdapter(mAdapter);
 
-        // Start out with a progress indicator.
-        // setListShown(false);
+		// Start out with a progress indicator.
+		// setListShown(false);
 
-        // Prepare the loader. Either re-connect with an existing one,
-        // or start a new one.
-        // getLoaderManager().initLoader(0, null, this)
+		// Prepare the loader. Either re-connect with an existing one,
+		// or start a new one.
+		// getLoaderManager().initLoader(0, null, this)
 
-        final Context context = getActivity();
-        ListView lv = getListView();
-        lv.setOnItemLongClickListener(new OnItemLongClickListener() {
-            @Override
-            public boolean onItemLongClick(AdapterView<?> av, View v, int pos, long id) {
-                Log.i(TAG, "On Long Click");
-                final CharSequence[] items = { "Hang up Call", "Send Message", "Add to Conference" };
-                final SipCall call = mAdapter.getItem(pos);
-                // // FIXME
-                // service = sflphoneApplication.getSipService();
-                AlertDialog.Builder builder = new AlertDialog.Builder(context);
-                builder.setTitle("Action to perform with " + call.mCallInfo.mDisplayName).setCancelable(true)
-                        .setItems(items, new DialogInterface.OnClickListener() {
-                            @Override
-                            public void onClick(DialogInterface dialog, int item) {
-                                Log.i(TAG, "Selected " + items[item]);
-                                switch (item) {
-                                case 0:
-                                    call.notifyServiceHangup(service);
-                                    break;
-                                case 1:
-                                    call.sendTextMessage();
-                                    // Need to hangup this call immediately since no way to do it after this action
-                                    call.notifyServiceHangup(service);
-                                    break;
-                                case 2:
-                                    call.addToConference();
-                                    // Need to hangup this call immediately since no way to do it after this action
-                                    call.notifyServiceHangup(service);
-                                    break;
-                                default:
-                                    break;
-                                }
-                            }
-                        });
-                AlertDialog alert = builder.create();
-                alert.show();
+		final Context context = getActivity();
+		ListView lv = getListView();
+		lv.setOnItemLongClickListener(new OnItemLongClickListener() {
+			@Override
+			public boolean onItemLongClick(AdapterView<?> av, View v, int pos, long id) {
+				Log.i(TAG, "On Long Click");
+				final CharSequence[] items = { "Hang up Call", "Send Message", "Add to Conference" };
+				final SipCall call = mAdapter.getItem(pos);
+				// // FIXME
+				// service = sflphoneApplication.getSipService();
+				AlertDialog.Builder builder = new AlertDialog.Builder(context);
+				builder.setTitle("Action to perform with " + call.mCallInfo.mDisplayName).setCancelable(true)
+				.setItems(items, new DialogInterface.OnClickListener() {
+					@Override
+					public void onClick(DialogInterface dialog, int item) {
+						Log.i(TAG, "Selected " + items[item]);
+						switch (item) {
+						case 0:
+							call.notifyServiceHangup(service);
+							break;
+						case 1:
+							call.sendTextMessage();
+							// Need to hangup this call immediately since no way to do it after this action
+							call.notifyServiceHangup(service);
+							break;
+						case 2:
+							call.addToConference();
+							// Need to hangup this call immediately since no way to do it after this action
+							call.notifyServiceHangup(service);
+							break;
+						default:
+							break;
+						}
+					}
+				});
+				AlertDialog alert = builder.create();
+				alert.show();
 
-                return true;
-            }
-        });
+				return true;
+			}
+		});
 
-        lv.setOnItemClickListener(new OnItemClickListener() {
+		lv.setOnItemClickListener(new OnItemClickListener() {
 
-            @Override
-            public void onItemClick(AdapterView<?> arg0, View v, int pos, long arg3) {
-                mCallbacks.onCallSelected(mAdapter.getItem(pos));
+			@Override
+			public void onItemClick(AdapterView<?> arg0, View v, int pos, long arg3) {
+				mCallbacks.onCallSelected(mAdapter.getItem(pos));
 
-            }
+			}
 
-        });
-    }
+		});
+	}
 
-    private void launchCallActivity(SipCall call) {
-        Log.i(TAG, "Launch Call Activity");
-        Bundle bundle = new Bundle();
-        bundle.putParcelable("CallInfo", call.mCallInfo);
-        Intent intent = new Intent().setClass(getActivity(), CallActivity.class);
-        intent.putExtras(bundle);
-        getActivity().startActivity(intent);
-    }
+	private void launchCallActivity(SipCall call) {
+		Log.i(TAG, "Launch Call Activity");
+		Bundle bundle = new Bundle();
+		bundle.putParcelable("CallInfo", call.mCallInfo);
+		Intent intent = new Intent().setClass(getActivity(), CallActivity.class);
+		intent.putExtras(bundle);
+		getActivity().startActivity(intent);
+	}
 
-    @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        inflater.inflate(R.menu.call_element_menu, menu);
+	@Override
+	public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+		inflater.inflate(R.menu.call_element_menu, menu);
 
-    }
+	}
 
-    private static final int REQUEST_CODE_PREFERENCES = 1;
+	private static final int REQUEST_CODE_PREFERENCES = 1;
 
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        Log.i(TAG, "onOptionsItemSelected " + item.getItemId());
-        switch (item.getItemId()) {
-        case R.id.menu_settings:
-            Intent launchPreferencesIntent = new Intent().setClass(getActivity(), SFLPhonePreferenceActivity.class);
-            startActivityForResult(launchPreferencesIntent, REQUEST_CODE_PREFERENCES);
-            break;
-        }
+	@Override
+	public boolean onOptionsItemSelected(MenuItem item) {
+		Log.i(TAG, "onOptionsItemSelected " + item.getItemId());
+		switch (item.getItemId()) {
+		case R.id.menu_settings:
+			Intent launchPreferencesIntent = new Intent().setClass(getActivity(), SFLPhonePreferenceActivity.class);
+			startActivityForResult(launchPreferencesIntent, REQUEST_CODE_PREFERENCES);
+			break;
+		}
 
-        return super.onOptionsItemSelected(item);
-    }
+		return super.onOptionsItemSelected(item);
+	}
 
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        Log.i(TAG, "onCreateView");
-        View inflatedView = inflater.inflate(R.layout.frag_call_element, container, false);
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+		Log.i(TAG, "onCreateView");
+		View inflatedView = inflater.inflate(R.layout.frag_call_element, container, false);
 
-        mAccountSelectionSpinner = (AccountSelectionSpinner) inflatedView.findViewById(R.id.account_selection_button);
-        phoneField = (EditText) inflatedView.findViewById(R.id.phoneNumberTextEntry);
-        buttonCall = (ImageButton) inflatedView.findViewById(R.id.buttonCall);
+		mAccountSelectionSpinner = (AccountSelectionSpinner) inflatedView.findViewById(R.id.account_selection_button);
+		phoneField = (EditText) inflatedView.findViewById(R.id.phoneNumberTextEntry);
+		buttonCall = (ImageButton) inflatedView.findViewById(R.id.buttonCall);
 
-        buttonCall.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                processingNewCallAction();
-            }
-        });
+		buttonCall.setOnClickListener(new OnClickListener() {
+			@Override
+			public void onClick(View v) {
+				processingNewCallAction();
+			}
+		});
 
-        attendedTransfer = (Button) inflatedView.findViewById(R.id.button_attended);
-        attendedTransfer.setOnClickListener(new OnClickListener() {
+		attendedTransfer = (Button) inflatedView.findViewById(R.id.button_attended);
+		attendedTransfer.setOnClickListener(new OnClickListener() {
 
-            @Override
-            public void onClick(View v) {
-                if (mAdapter.getCount() == 2) {
-                    try {
-                        service.attendedTransfer(mAdapter.getItem(0).getCallId(), mAdapter.getItem(1).getCallId());
-                        mAdapter.clear();
-                    } catch (RemoteException e) {
-                        Log.e(TAG, e.toString());
-                    }
-                } else {
-                    Toast.makeText(getActivity(), "You need two calls one on Hold the other current to bind them", Toast.LENGTH_LONG).show();
-                }
+			@Override
+			public void onClick(View v) {
+				if (mAdapter.getCount() == 2) {
+					try {
+						service.attendedTransfer(mAdapter.getItem(0).getCallId(), mAdapter.getItem(1).getCallId());
+						mAdapter.clear();
+					} catch (RemoteException e) {
+						Log.e(TAG, e.toString());
+					}
+				} else {
+					Toast.makeText(getActivity(), "You need two calls one on Hold the other current to bind them", Toast.LENGTH_LONG).show();
+				}
 
-            }
-        });
+			}
+		});
 
-        conference = (Button) inflatedView.findViewById(R.id.button_conf);
-        conference.setOnClickListener(new OnClickListener() {
+		conference = (Button) inflatedView.findViewById(R.id.button_conf);
+		conference.setOnClickListener(new OnClickListener() {
 
-            @Override
-            public void onClick(View v) {
-                if (mAdapter.getCount() == 2) {
-                    try {
-                        service.joinParticipant(mAdapter.getItem(0).getCallId(), mAdapter.getItem(1).getCallId());
-                    } catch (RemoteException e) {
-                        Log.e(TAG, e.toString());
-                    }
-                } else {
-                    Toast.makeText(getActivity(), "You need two calls one on Hold the other current to create a conference", Toast.LENGTH_LONG)
-                            .show();
-                }
-            }
-        });
+			@Override
+			public void onClick(View v) {
+				if (mAdapter.getCount() == 2) {
+					try {
+						service.joinParticipant(mAdapter.getItem(0).getCallId(), mAdapter.getItem(1).getCallId());
+					} catch (RemoteException e) {
+						Log.e(TAG, e.toString());
+					}
+				} else {
+					Toast.makeText(getActivity(), "You need two calls one on Hold the other current to create a conference", Toast.LENGTH_LONG)
+					.show();
+				}
+			}
+		});
 
-        switchHold = (ToggleButton) inflatedView.findViewById(R.id.switch_hold);
-        switchHold.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+		switchHold = (ToggleButton) inflatedView.findViewById(R.id.switch_hold);
+		switchHold.setOnCheckedChangeListener(new OnCheckedChangeListener() {
 
-            @Override
-            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                try {
-                    ArrayList<String> confList = (ArrayList<String>) service.getConferenceList();
-                    if (!confList.isEmpty()) {
-                        if (isChecked) {
-                            service.holdConference(confList.get(0));
-                        } else {
-                            service.unholdConference(confList.get(0));
-                        }
-                    }
-                } catch (RemoteException e) {
-                    Log.e(TAG, e.toString());
-                }
+			@Override
+			public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+				try {
+					ArrayList<String> confList = (ArrayList<String>) service.getConferenceList();
+					if (!confList.isEmpty()) {
+						if (isChecked) {
+							service.holdConference(confList.get(0));
+						} else {
+							service.unholdConference(confList.get(0));
+						}
+					}
+				} catch (RemoteException e) {
+					Log.e(TAG, e.toString());
+				}
 
-            }
-        });
+			}
+		});
 
-        isReady = true;
-        if (mCallbacks.getService() != null) {
+		isReady = true;
+		if (mCallbacks.getService() != null) {
 
-            onServiceSipBinded(mCallbacks.getService());
-        }
-        return inflatedView;
-    }
+			onServiceSipBinded(mCallbacks.getService());
+		}
+		return inflatedView;
+	}
 
-    public void processingNewCallAction() {
-        // String accountID = mAccountList.currentAccountID;
-        String accountID = mAccountSelectionSpinner.getAccount();
+	public void processingNewCallAction() {
+		// String accountID = mAccountList.currentAccountID;
+		String accountID = mAccountSelectionSpinner.getAccount();
 
-        String to = phoneField.getText().toString();
+		String to = phoneField.getText().toString();
 
-        Random random = new Random();
-        String callID = Integer.toString(random.nextInt());
-        SipCall.CallInfo info = new SipCall.CallInfo();
+		Random random = new Random();
+		String callID = Integer.toString(random.nextInt());
+		SipCall.CallInfo info = new SipCall.CallInfo();
 
-        info.mCallID = callID;
-        info.mAccountID = accountID;
-        info.mDisplayName = "Cool Guy!";
-        info.mPhone = to;
-        info.mEmail = "coolGuy@coolGuy.com";
-        info.mCallType = SipCall.CALL_TYPE_OUTGOING;
+		info.mCallID = callID;
+		info.mAccountID = accountID;
+		info.mDisplayName = "Cool Guy!";
+		info.mPhone = to;
+		info.mEmail = "coolGuy@coolGuy.com";
+		info.mCallType = SipCall.CALL_TYPE_OUTGOING;
 
-        SipCall call = CallListReceiver.getCallInstance(info);
-        mCallbacks.onCallSelected(call);
+		SipCall call = CallListReceiver.getCallInstance(info);
+		mCallbacks.onCallSelected(call);
 
-        try {
-            service.placeCall(info.mAccountID, info.mCallID, info.mPhone);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Cannot call service method", e);
-        }
-        addCall(call);
-    }
+		try {
+			service.placeCall(info.mAccountID, info.mCallID, info.mPhone);
+		} catch (RemoteException e) {
+			Log.e(TAG, "Cannot call service method", e);
+		}
+		addCall(call);
+	}
 
-    @Override
-    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+	@Override
+	public Loader<Cursor> onCreateLoader(int id, Bundle args) {
 
-        Log.i(TAG, "onCreateLoader");
-        Uri baseUri;
+		Log.i(TAG, "onCreateLoader");
+		Uri baseUri;
 
-        if (mCurFilter != null) {
-            baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(mCurFilter));
-        } else {
-            baseUri = Contacts.CONTENT_URI;
-        }
+		if (mCurFilter != null) {
+			baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(mCurFilter));
+		} else {
+			baseUri = Contacts.CONTENT_URI;
+		}
 
-        // Now create and return a CursorLoader that will take care of
-        // creating a Cursor for the data being displayed.
-        String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME
-                + " != '' ))";
-        // String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.DISPLAY_NAME + " != '' ))";
+		// Now create and return a CursorLoader that will take care of
+		// creating a Cursor for the data being displayed.
+		String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME
+				+ " != '' ))";
+		// String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.DISPLAY_NAME + " != '' ))";
 
-        return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
-    }
+		return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
+	}
 
-    @Override
-    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
-        Log.i(TAG, "onLoadFinished");
-        // Swap the new cursor in. (The framework will take care of closing the
-        // old cursor once we return.)
-        // mAdapter.swapCursor(data);
+	@Override
+	public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+		Log.i(TAG, "onLoadFinished");
+		// Swap the new cursor in. (The framework will take care of closing the
+		// old cursor once we return.)
+		// mAdapter.swapCursor(data);
 
-        // The list should now be shown.
-        /*
-         * if (isResumed()) { setListShown(true); } else { setListShownNoAnimation(true); }
-         */
-    }
+		// The list should now be shown.
+		/*
+		 * if (isResumed()) { setListShown(true); } else { setListShownNoAnimation(true); }
+		 */
+	}
 
-    @Override
-    public void onLoaderReset(Loader<Cursor> loader) {
-        // This is called when the last Cursor provided to onLoadFinished()
-        // above is about to be closed. We need to make sure we are no
-        // longer using it.
-        // mAdapter.swapCursor(null);
-    }
+	@Override
+	public void onLoaderReset(Loader<Cursor> loader) {
+		// This is called when the last Cursor provided to onLoadFinished()
+		// above is about to be closed. We need to make sure we are no
+		// longer using it.
+		// mAdapter.swapCursor(null);
+	}
 
-    /**
-     * Called by activity to pass a reference to sipservice to Fragment.
-     * 
-     * @param isip
-     */
-    public void onServiceSipBinded(ISipService isip) {
+	/**
+	 * Called by activity to pass a reference to sipservice to Fragment.
+	 * 
+	 * @param isip
+	 */
+	public void onServiceSipBinded(ISipService isip) {
 
-        if (isReady) {
-            service = isip;
-            ArrayList<String> accountList;
-            try {
-                accountList = (ArrayList<String>) mCallbacks.getService().getAccountList();
-                mAccountSelectionSpinner.populate(mCallbacks.getService(), accountList);
-            } catch (RemoteException e) {
-                Log.i(TAG, e.toString());
-            }
-        }
+		if (isReady) {
+			service = isip;
+			ArrayList<String> accountList;
+			try {
+				accountList = (ArrayList<String>) mCallbacks.getService().getAccountList();
+				mAccountSelectionSpinner.populate(mCallbacks.getService(), accountList);
+			} catch (RemoteException e) {
+				Log.i(TAG, e.toString());
+			}
+		}
 
-    }
+	}
 
-    public void updateCall(String iD, String newState) {
-        mAdapter.update(iD, newState);
+	public void updateCall(String iD, String newState) {
+		mAdapter.update(iD, newState);
 
-    }
+	}
 
 }
diff --git a/src/com/savoirfairelinux/sflphone/model/Bubble.java b/src/com/savoirfairelinux/sflphone/model/Bubble.java
index 41dc4bc..0dbd013 100644
--- a/src/com/savoirfairelinux/sflphone/model/Bubble.java
+++ b/src/com/savoirfairelinux/sflphone/model/Bubble.java
@@ -15,12 +15,15 @@
 
 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 @@
 		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 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 @@
 		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.
 	 */
diff --git a/src/com/savoirfairelinux/sflphone/model/BubbleModel.java b/src/com/savoirfairelinux/sflphone/model/BubbleModel.java
index 5e785ae..460d9e9 100644
--- a/src/com/savoirfairelinux/sflphone/model/BubbleModel.java
+++ b/src/com/savoirfairelinux/sflphone/model/BubbleModel.java
@@ -3,7 +3,6 @@
 import java.util.ArrayList;
 
 import android.graphics.PointF;
-import android.util.Log;
 
 public class BubbleModel
 {
@@ -14,18 +13,35 @@
 	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;	// 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
-	 */
+	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;
+	}
+
 	public void update()
 	{
 		long now = System.nanoTime();
@@ -65,76 +81,66 @@
 					}
 				}
 
-				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);
+				//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;
+					}
+
+				}
+
+				// 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;
+				}
+
+
+				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) {
+				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);
-
-				b.speed.x *= friction_coef;
-				b.speed.y *= friction_coef;
-
-
-				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);
-
-				}
-				// 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.setScale(b.getScale() + (b.target_scale-b.getScale())*dt*10.f);
+
 		}
 	}
 }
diff --git a/src/com/savoirfairelinux/sflphone/model/BubblesView.java b/src/com/savoirfairelinux/sflphone/model/BubblesView.java
index e0472cc..1243275 100644
--- a/src/com/savoirfairelinux/sflphone/model/BubblesView.java
+++ b/src/com/savoirfairelinux/sflphone/model/BubblesView.java
@@ -4,6 +4,8 @@
 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 @@
 	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 @@
 

 		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 @@
 				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 @@
 			for (Bubble b : model.listBubbles) {

 				if (b.dragged) {

 					b.dragged = false;

+					b.target_scale = 1.f;

 				}

 			}

 		}

@@ -236,7 +251,17 @@
 

 				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);

 				}

 			}

 		}