Bubbles: Honor screen density when drawing bubbles and attractors.
diff --git a/src/com/savoirfairelinux/sflphone/fragments/CallFragment.java b/src/com/savoirfairelinux/sflphone/fragments/CallFragment.java
index b9ee971..35f0b11 100644
--- a/src/com/savoirfairelinux/sflphone/fragments/CallFragment.java
+++ b/src/com/savoirfairelinux/sflphone/fragments/CallFragment.java
@@ -36,6 +36,7 @@
 import android.app.Activity;
 import android.app.Fragment;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.graphics.PointF;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -54,264 +55,289 @@
 import com.savoirfairelinux.sflphone.model.BubblesView;
 import com.savoirfairelinux.sflphone.model.CallContact;
 import com.savoirfairelinux.sflphone.model.SipCall;
-import com.savoirfairelinux.sflphone.service.CallManagerCallBack;
 import com.savoirfairelinux.sflphone.service.ISipService;
 
 public class CallFragment extends Fragment {
-    static final String TAG = "CallFragment";
 
-    private SipCall mCall;
+	static final String TAG = "CallFragment";
 
-    private BubblesView view;
-    private BubbleModel model;
-    private PointF screenCenter;
-    private DisplayMetrics metrics;
+	static final float BUBBLE_SIZE = 100;
 
-    private Callbacks mCallbacks = sDummyCallbacks;
+	private SipCall mCall;
 
-    private HashMap<CallContact, Bubble> contacts = new HashMap<CallContact, Bubble>();
+	private BubblesView view;
+	private BubbleModel model;
+	private PointF screenCenter;
+	private DisplayMetrics metrics;
 
-    private TextView contact_name_txt;
+	private Callbacks mCallbacks = sDummyCallbacks;
 
-    CallContact myself = CallContact.ContactBuilder.buildUserContact("Me");
+	private HashMap<CallContact, Bubble> contacts = new HashMap<CallContact, Bubble>();
 
-    @Override
-    public void onCreate(Bundle savedBundle) {
-        super.onCreate(savedBundle);
-        model = new BubbleModel(getResources().getDisplayMetrics().density);
-        metrics = getResources().getDisplayMetrics();
-        screenCenter = new PointF(metrics.widthPixels / 2, metrics.heightPixels / 3);
+	private TextView contact_name_txt;
 
-        Bundle b = getArguments();
+	private CallContact myself = CallContact.ContactBuilder.buildUserContact("Me");
 
-        mCall = b.getParcelable("CallInfo");
-    }
+	private Bitmap hangup_icon;
+	private Bitmap call_icon;
 
-    /**
-     * 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 onCreate(Bundle savedBundle) {
+		super.onCreate(savedBundle);
+		model = new BubbleModel(getResources().getDisplayMetrics().density);
+		metrics = getResources().getDisplayMetrics();
+		screenCenter = new PointF(metrics.widthPixels / 2, metrics.heightPixels / 3);
 
-        @Override
-        public void onSendMessage(SipCall call, String msg) {
+		Bundle b = getArguments();
 
-        }
+		mCall = b.getParcelable("CallInfo");
+		//mCall.
+	}
 
-        @Override
-        public void callContact(SipCall call) {
-        }
+	/**
+	 * 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 onSendMessage(SipCall call, String msg) {
+		}
 
-        @Override
-        public void onCallAccepted(SipCall call) {
-            // TODO Auto-generated method stub
+		@Override
+		public void callContact(SipCall call) {
+		}
 
-        }
+		@Override
+		public void onCallAccepted(SipCall call) {
+		}
 
-        @Override
-        public void onCallRejected(SipCall call) {
-            // TODO Auto-generated method stub
+		@Override
+		public void onCallRejected(SipCall call) {
+		}
 
-        }
+		@Override
+		public void onCallEnded(SipCall call) {
+		}
 
-        @Override
-        public void onCallEnded(SipCall call) {
-            // TODO Auto-generated method stub
+		@Override
+		public void onCallSuspended(SipCall call) {
+		}
 
-        }
+		@Override
+		public void onCallResumed(SipCall call) {
+		}
 
-        @Override
-        public void onCallSuspended(SipCall call) {
-            // TODO Auto-generated method stub
+		@Override
+		public void onCalltransfered(SipCall call, String to) {
+		}
 
-        }
+		@Override
+		public void onRecordCall(SipCall call) {
+		}
 
-        @Override
-        public void onCallResumed(SipCall call) {
-            // TODO Auto-generated method stub
+		@Override
+		public ISipService getService() {
+			return null;
+		}
+	};
 
-        }
+	/**
+	 * The Activity calling this fragment has to implement this interface
+	 * 
+	 */
+	public interface Callbacks {
 
-        @Override
-        public void onCalltransfered(SipCall call, String to) {
-            // TODO Auto-generated method stub
+		public ISipService getService();
 
-        }
+		public void callContact(SipCall call);
 
-        @Override
-        public void onRecordCall(SipCall call) {
-            // TODO Auto-generated method stub
+		public void onCallAccepted(SipCall call);
 
-        }
+		public void onCallRejected(SipCall call);
 
-        @Override
-        public ISipService getService() {
-            // TODO Auto-generated method stub
-            return null;
-        }
-    };
+		public void onCallEnded(SipCall call);
 
-    /**
-     * The Activity calling this fragment has to implement this interface
-     * 
-     */
-    public interface Callbacks {
+		public void onCallSuspended(SipCall call);
 
-        public ISipService getService();
+		public void onCallResumed(SipCall call);
 
-        public void callContact(SipCall call);
+		public void onCalltransfered(SipCall call, String to);
 
-        public void onCallAccepted(SipCall call);
+		public void onRecordCall(SipCall call);
 
-        public void onCallRejected(SipCall call);
+		public void onSendMessage(SipCall call, String msg);
+	}
 
-        public void onCallEnded(SipCall call);
+	@Override
+	public void onAttach(Activity activity) {
+		super.onAttach(activity);
 
-        public void onCallSuspended(SipCall call);
+		if (!(activity instanceof Callbacks)) {
+			throw new IllegalStateException("Activity must implement fragment's callbacks.");
+		}
 
-        public void onCallResumed(SipCall call);
+		mCallbacks = (Callbacks) activity;
+	}
 
-        public void onCalltransfered(SipCall call, String to);
+	@Override
+	public void onDetach() {
+		super.onDetach();
+		mCallbacks = sDummyCallbacks;
+	}
 
-        public void onRecordCall(SipCall call);
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+		ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.frag_call, container, false);
 
-        public void onSendMessage(SipCall call, String msg);
-    }
+		view = (BubblesView) rootView.findViewById(R.id.main_view);
+		view.setModel(model);
 
-    @Override
-    public void onAttach(Activity activity) {
-        super.onAttach(activity);
+		hangup_icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_hangup);
+		call_icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_call);
 
-        if (!(activity instanceof Callbacks)) {
-            throw new IllegalStateException("Activity must implement fragment's callbacks.");
-        }
+		Log.i(TAG, "Starting fragment for call " + mCall.getCallId());
 
-        mCallbacks = (Callbacks) activity;
-    }
+		mCall.printCallInfo();
 
-    @Override
-    public void onDetach() {
-        super.onDetach();
-        mCallbacks = sDummyCallbacks;
-    }
+		if (mCall.isIncoming() && mCall.isRinging()) {
+			initIncomingCallDisplay();
+		} else {
+			if (mCall.isRinging()) {
+				initOutGoingCallDisplay();
+			}
+			try {
+				if (mCall.isOutGoing() && mCallbacks.getService().getCall(mCall.getCallId()) == null) {
+					mCallbacks.getService().placeCall(mCall);
+					initOutGoingCallDisplay();
+				} else if(mCall.isOutGoing() && mCall.isRinging()){
+					initOutGoingCallDisplay();
+				}
+			} catch (RemoteException e) {
+				Log.e(TAG, e.toString());
+			}
+		}
 
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.frag_call, container, false);
+		if(mCall.isOngoing()){
+			initNormalStateDisplay();
+		}
 
-        view = (BubblesView) rootView.findViewById(R.id.main_view);
-        view.setModel(model);
+		return rootView;
+	}
 
-        Log.i(TAG, "Starting fragment for call " + mCall.getCallId());
+	private void initNormalStateDisplay() {
+		Log.i(TAG, "Start normal display");
+		// TODO off-thread image loading
+		Bubble contact_bubble = getBubbleFor(mCall.getContacts().get(0), screenCenter.x, screenCenter.y);
+		Bubble me = getBubbleFor(myself, screenCenter.x, screenCenter.y * 3 / 2);
 
-        mCall.printCallInfo();
-        if (mCall.isRinging()) {
-            initOutGoingCallDisplay();
-        }
-        if (mCall.isIncoming() && mCall.isRinging()) {
-            initIncomingCallDisplay();
-        }
-        try {
-            if (mCall.isOutGoing() && mCallbacks.getService().getCall(mCall.getCallId()) == null) {
-                mCallbacks.getService().placeCall(mCall);
-                initOutGoingCallDisplay();
-            } else if(mCall.isOutGoing() && mCall.isRinging()){
-                initOutGoingCallDisplay();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, e.toString());
-        }
+		/*	contact_bubble.setPos(screenCenter.x, screenCenter.y);
+		me.setPos(screenCenter.x, screenCenter.y * 3 / 2);*/
+		/*if (mCall.getContacts().get(0).getPhoto_id() > 0) {
+			Bitmap photo = ContactPictureLoader.loadContactPhoto(getActivity().getContentResolver(), mCall.getContacts().get(0).getId());
+			contact_bubble = new Bubble(screenCenter.x, screenCenter.y, 150, photo);
+		} else {
+			contact_bubble = new Bubble(getActivity(), screenCenter.x, screenCenter.y / 2 , 150, R.drawable.ic_contact_picture);
+		}
 
-        if(mCall.isOngoing()){
-            initNormalStateDisplay();
-        }
+		me = new Bubble(getActivity(), screenCenter.x, screenCenter.y * 3 / 2, 150, R.drawable.ic_contact_picture);
+		 */
+		model.clearAttractors();
+		model.addAttractor(new Attractor(new PointF(metrics.widthPixels / 2, metrics.heightPixels * .8f), 20, new Attractor.Callback() {
+			@Override
+			public void onBubbleSucked(Bubble b) {
+				Log.w(TAG, "Bubble sucked ! ");
+				mCallbacks.onCallEnded(mCall);
+			}
+		}, hangup_icon));
 
-        return rootView;
-    }
+		/*	contact_bubble.contact = mCall.getContacts().get(0);
+		me.contact = myself;
+		model.addBubble(contact_bubble);
+		model.addBubble(me);
+		contacts.put(mCall.getContacts().get(0), contact_bubble);
+		contacts.put(myself, me);*/
+	}
 
-    private void initNormalStateDisplay() {
-        Log.i(TAG, "Start normal display");
-        // TODO off-thread image loading
-        Bubble contact_bubble, me;
-        if (mCall.getContacts().get(0).getPhoto_id() > 0) {
-            Bitmap photo = ContactPictureLoader.loadContactPhoto(getActivity().getContentResolver(), mCall.getContacts().get(0).getId());
-            contact_bubble = new Bubble(getActivity(), screenCenter.x, screenCenter.y, 150, photo);
-        } else {
-            contact_bubble = new Bubble(getActivity(), screenCenter.x, screenCenter.y / 2 , 150, R.drawable.ic_contact_picture);
-        }
+	private void initIncomingCallDisplay() {
+		Log.i(TAG, "Start incoming display");
 
-        me = new Bubble(getActivity(), screenCenter.x, screenCenter.y * 3 / 2, 150, R.drawable.ic_contact_picture);
+		Bubble contact_bubble = getBubbleFor(mCall.getContacts().get(0), screenCenter.x, screenCenter.y);
+		contacts.put(mCall.getContacts().get(0), contact_bubble);
 
-        model.attractors.clear();
-        model.attractors.add(new Attractor(new PointF(metrics.widthPixels / 2, metrics.heightPixels * .8f), new Attractor.Callback() {
-            @Override
-            public void onBubbleSucked(Bubble b) {
-                Log.w(TAG, "Bubble sucked ! ");
-                mCallbacks.onCallEnded(mCall);
-            }
-        }));
+		model.clearAttractors();
+		model.addAttractor(new Attractor(new PointF(3 * metrics.widthPixels / 4, metrics.heightPixels / 4), 20, new Attractor.Callback() {
+			@Override
+			public void onBubbleSucked(Bubble b) {
+				mCallbacks.onCallAccepted(mCall);
+			}
+		}, call_icon));
+		model.addAttractor(new Attractor(new PointF(metrics.widthPixels / 4, metrics.heightPixels / 4), 20, new Attractor.Callback() {
+			@Override
+			public void onBubbleSucked(Bubble b) {
+				mCallbacks.onCallRejected(mCall);
+			}
+		}, hangup_icon));
+	}
 
-        contact_bubble.contact = mCall.getContacts().get(0);
-        me.contact = myself;
-        model.listBubbles.add(contact_bubble);
-        model.listBubbles.add(me);
-        contacts.put(mCall.getContacts().get(0), contact_bubble);
-        contacts.put(myself, me);
+	private void initOutGoingCallDisplay() {
+		Log.i(TAG, "Start outgoing display");
+		// TODO off-thread image loading
+		Bubble contact_bubble = getBubbleFor(mCall.getContacts().get(0), screenCenter.x, screenCenter.y);
 
-    }
+		model.clearAttractors();
+		model.addAttractor(new Attractor(new PointF(metrics.widthPixels / 2, metrics.heightPixels * .8f), 20, new Attractor.Callback() {
+			@Override
+			public void onBubbleSucked(Bubble b) {
+				Log.w(TAG, "Bubble sucked ! ");
+				mCallbacks.onCallEnded(mCall);
+			}
+		}, hangup_icon));
 
-    private void initIncomingCallDisplay() {
-        Log.i(TAG, "Start incoming display");
-        model.attractors.clear();
-        model.attractors.add(new Attractor(new PointF(3 * metrics.widthPixels / 4, metrics.heightPixels / 4), new Attractor.Callback() {
-            @Override
-            public void onBubbleSucked(Bubble b) {
-                mCallbacks.onCallAccepted(mCall);
-            }
-        }));
-        model.attractors.add(new Attractor(new PointF(metrics.widthPixels / 4, metrics.heightPixels / 4), new Attractor.Callback() {
-            @Override
-            public void onBubbleSucked(Bubble b) {
-                mCallbacks.onCallRejected(mCall);
-            }
-        }));
-    }
+		/*contact_bubble.contact = mCall.getContacts().get(0);
+		model.addBubble(contact_bubble);
+		contacts.put(mCall.getContacts().get(0), contact_bubble);*/
+	}
 
-    private void initOutGoingCallDisplay() {
-        Log.i(TAG, "Start outgoing display");
-        // TODO off-thread image loading
-        Bubble contact_bubble;
-        if (mCall.getContacts().get(0).getPhoto_id() > 0) {
-            Bitmap photo = ContactPictureLoader.loadContactPhoto(getActivity().getContentResolver(), mCall.getContacts().get(0).getId());
-            contact_bubble = new Bubble(getActivity(), screenCenter.x, screenCenter.y, 150, photo);
-        } else {
-            contact_bubble = new Bubble(getActivity(), screenCenter.x, screenCenter.y, 150, R.drawable.ic_contact_picture);
-        }
+	/**
+	 * Retrieves or create a bubble for a given contact.
+	 * If the bubble exists, it is moved to the new location.
+	 * 
+	 * @param contact The contact
+	 * @param x Initial or new x position.
+	 * @param y Initial or new y position.
+	 * @return Bubble corresponding to the contact.
+	 */
+	private Bubble getBubbleFor(CallContact contact, float x, float y) {
+		Bubble contact_bubble = contacts.get(contact);
+		if(contact_bubble != null) {
+			contact_bubble.attractor.set(x, y);
+			return contact_bubble;
+		}
 
-        model.attractors.clear();
-        model.attractors.add(new Attractor(new PointF(metrics.widthPixels / 2, metrics.heightPixels * .8f), new Attractor.Callback() {
-            @Override
-            public void onBubbleSucked(Bubble b) {
-                Log.w(TAG, "Bubble sucked ! ");
-                mCallbacks.onCallEnded(mCall);
-            }
-        }));
+		if (contact.getPhoto_id() > 0) {
+			Bitmap photo = ContactPictureLoader.loadContactPhoto(getActivity().getContentResolver(), mCall.getContacts().get(0).getId());
+			contact_bubble = new Bubble(x, y, BUBBLE_SIZE, photo);
+		} else {
+			contact_bubble = new Bubble(x, y, BUBBLE_SIZE, getActivity(), R.drawable.ic_contact_picture);
+		}
+		contact_bubble.contact = contact;
 
-        contact_bubble.contact = mCall.getContacts().get(0);
-        model.listBubbles.add(contact_bubble);
-        contacts.put(mCall.getContacts().get(0), contact_bubble);
-    }
+		model.addBubble(contact_bubble);
+		contacts.put(contact, contact_bubble);
 
-    public void changeCallState(String callID, String newState) {
-        
-        Log.w(TAG, "Changing call state of "+callID);
-        mCall.printCallInfo();
-        if(callID != mCall.getCallId())
-            return;
-        
-        mCall.setCallState(newState);
-        if(mCall.isOngoing()){
-            initNormalStateDisplay();
-        }
-    }
+		return contact_bubble;
+	}
+
+	public void changeCallState(String callID, String newState) {
+
+		Log.w(TAG, "Changing call state of "+callID);
+		mCall.printCallInfo();
+		if(!callID.equals(mCall.getCallId()))
+			return;
+
+		mCall.setCallState(newState);
+		if(mCall.isOngoing()){
+			initNormalStateDisplay();
+		}
+	}
 
 }