* #38608: better handling of conferences

Home interface now only have one list to display conferences (renamed conversations)
There are still some issues with conference mapping client side.
diff --git a/res/layout/frag_call_list.xml b/res/layout/frag_call_list.xml
index e5de2c2..dec110d 100644
--- a/res/layout/frag_call_list.xml
+++ b/res/layout/frag_call_list.xml
@@ -35,51 +35,9 @@
     android:layout_height="match_parent" >
 
     <RelativeLayout
-        android:id="@+id/calls_layouts"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_alignParentTop="true"
-        android:layout_margin="10dp"
-        android:background="@drawable/item_generic_selector" >
-
-        <LinearLayout
-            android:id="@+id/linear1"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_alignParentLeft="true"
-            android:layout_alignParentTop="true"
-            android:weightSum="4" >
-
-            <TextView
-                android:id="@+id/calls_counter"
-                android:layout_width="0dp"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:gravity="center"
-                android:textSize="40sp" />
-
-            <TextView
-                android:id="@+id/textView2"
-                android:layout_width="0dp"
-                android:layout_height="wrap_content"
-                android:layout_weight="3"
-                android:text="@string/home_calls_title"
-                android:textSize="30sp" />
-        </LinearLayout>
-
-        <ListView
-            android:id="@+id/calls_list"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_below="@+id/linear1" >
-        </ListView>
-    </RelativeLayout>
-
-    <RelativeLayout
         android:id="@+id/confs_layouts"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_below="@+id/calls_layouts"
         android:layout_margin="10dp"
         android:background="@drawable/item_generic_selector" >
 
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 0c4fb64..88fbe80 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -76,7 +76,7 @@
     <string name="detail_hist_call_number">Appeler %1$s</string>
 
     <!-- Home Fragment -->
-    <string name="home_conferences_title">Conférences</string>
+    <string name="home_conferences_title">Conversations</string>
     <string name="home_calls_title">Appels</string>
     <string name="home_transfering">Transfert de %1$s à %1$s</string>
     <string name="home_transfer_complet">Transfert terminé</string>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3edc96c..5c4052b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -76,7 +76,7 @@
     <string name="detail_hist_call_number">Call %1$s</string>
 
     <!-- Home Fragment -->
-    <string name="home_conferences_title">Conferences</string>
+    <string name="home_conferences_title">Conversations</string>
     <string name="home_calls_title">Calls</string>
     <string name="home_transfering">Transferring %1$s to %1$s</string>
     <string name="home_transfer_complet">Transfer complete</string>
diff --git a/src/org/sflphone/adapters/SectionsPagerAdapter.java b/src/org/sflphone/adapters/SectionsPagerAdapter.java
index 485c005..a92ccad 100644
--- a/src/org/sflphone/adapters/SectionsPagerAdapter.java
+++ b/src/org/sflphone/adapters/SectionsPagerAdapter.java
@@ -112,16 +112,6 @@
         return null;
     }
 
-    public void updateHome() {
-        try {
-            ((CallListFragment) fragments.get(1)).updateLists();
-        } catch (RemoteException e) {
-            e.printStackTrace();
-        } catch (Exception e1) {
-            e1.printStackTrace();
-        }
-    }
-
     @Override
     public int getPageIconResId(int position) {
         switch (position) {
diff --git a/src/org/sflphone/client/HomeActivity.java b/src/org/sflphone/client/HomeActivity.java
index b56dce6..8581959 100644
--- a/src/org/sflphone/client/HomeActivity.java
+++ b/src/org/sflphone/client/HomeActivity.java
@@ -97,7 +97,7 @@
     private ISipService service;
 
     public static final int REQUEST_CODE_PREFERENCES = 1;
-    private static final int REQUEST_CODE_CALL = 3;
+    public static final int REQUEST_CODE_CALL = 3;
 
     SlidingUpPanelLayout mContactDrawer;
     private DrawerLayout mNavigationDrawer;
@@ -598,14 +598,6 @@
     }
 
     @Override
-    public void selectedCall(Conference c) {
-        Intent intent = new Intent().setClass(this, CallActivity.class);
-        intent.putExtra("resuming", true);
-        intent.putExtra("conference", c);
-        startActivityForResult(intent, REQUEST_CODE_CALL);
-    }
-
-    @Override
     public void setDragView(RelativeLayout relativeLayout) {
         mContactDrawer.setDragView(relativeLayout);
     }
diff --git a/src/org/sflphone/fragments/CallListFragment.java b/src/org/sflphone/fragments/CallListFragment.java
index a324b6a..ea63b7e 100644
--- a/src/org/sflphone/fragments/CallListFragment.java
+++ b/src/org/sflphone/fragments/CallListFragment.java
@@ -30,32 +30,15 @@
  */
 package org.sflphone.fragments;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Observable;
-import java.util.Observer;
-
-import android.content.IntentFilter;
-import org.sflphone.R;
-import org.sflphone.interfaces.CallInterface;
-import org.sflphone.model.CallTimer;
-import org.sflphone.model.Conference;
-import org.sflphone.receivers.CallReceiver;
-import org.sflphone.service.CallManagerCallBack;
-import org.sflphone.service.ISipService;
-
 import android.app.Activity;
+import android.app.Fragment;
 import android.content.ClipData;
 import android.content.ClipData.Item;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.graphics.Color;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.Vibrator;
-import android.app.Fragment;
+import android.os.*;
 import android.util.Log;
 import android.view.DragEvent;
 import android.view.LayoutInflater;
@@ -63,21 +46,30 @@
 import android.view.View.DragShadowBuilder;
 import android.view.View.OnDragListener;
 import android.view.ViewGroup;
-import android.widget.AdapterView;
+import android.widget.*;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.AdapterView.OnItemLongClickListener;
-import android.widget.BaseAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
+import org.sflphone.R;
+import org.sflphone.client.CallActivity;
+import org.sflphone.client.HomeActivity;
+import org.sflphone.interfaces.CallInterface;
+import org.sflphone.model.Conference;
+import org.sflphone.receivers.CallReceiver;
+import org.sflphone.service.CallManagerCallBack;
+import org.sflphone.service.ISipService;
 
-public class CallListFragment extends Fragment implements CallInterface{
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Observable;
+import java.util.Observer;
+
+public class CallListFragment extends Fragment implements CallInterface {
+
     private static final String TAG = CallListFragment.class.getSimpleName();
 
     private Callbacks mCallbacks = sDummyCallbacks;
-    private TextView nb_calls, nb_confs;
-    CallListAdapter confs_adapter, calls_adapter;
-    CallTimer timer;
+    private TextView nb_confs;
+    CallListAdapter confs_adapter;
     CallReceiver callReceiver;
 
     public static final int REQUEST_TRANSFER = 10;
@@ -94,9 +86,6 @@
             return null;
         }
 
-        @Override
-        public void selectedCall(Conference c) {
-        }
     };
 
     @Override
@@ -105,11 +94,9 @@
         String cID = b.getString("CallID");
         String state = b.getString("State");
         Log.i(TAG, "callStateChanged" + cID + "    " + state);
-        try {
-            updateLists();
-        } catch (RemoteException e) {
-            e.printStackTrace();
-        }
+
+        updateLists();
+
     }
 
     @Override
@@ -123,21 +110,20 @@
 
     @Override
     public void confCreated(Intent intent) {
-        try {
-            updateLists();
-        } catch (RemoteException e) {
-            e.printStackTrace();
-        }
+        Log.i(TAG, "confCreated");
+        updateLists();
     }
 
     @Override
     public void confRemoved(Intent intent) {
-
+        Log.i(TAG, "confRemoved");
+        updateLists();
     }
 
     @Override
     public void confChanged(Intent intent) {
-
+        Log.i(TAG, "confChanged");
+        updateLists();
     }
 
     @Override
@@ -149,11 +135,7 @@
      * The Activity calling this fragment has to implement this interface
      */
     public interface Callbacks {
-
         public ISipService getService();
-
-        public void selectedCall(Conference c);
-
     }
 
     @Override
@@ -176,7 +158,6 @@
             int minutes = seconds / 60;
             seconds = seconds % 60;
 
-            calls_adapter.notifyDataSetChanged();
             confs_adapter.notifyDataSetChanged();
             mHandler.postAtTime(this, start + (((minutes * 60) + seconds + 1) * 1000));
         }
@@ -193,14 +174,10 @@
         intentFilter.addAction(CallManagerCallBack.CALL_STATE_CHANGED);
         getActivity().registerReceiver(callReceiver, intentFilter);
         if (mCallbacks.getService() != null) {
-            try {
-                updateLists();
-                if (!calls_adapter.isEmpty() || !confs_adapter.isEmpty()) {
-                    mHandler.postDelayed(mUpdateTimeTask, 0);
-                }
 
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
+            updateLists();
+            if (!confs_adapter.isEmpty()) {
+                mHandler.postDelayed(mUpdateTimeTask, 0);
             }
         }
 
@@ -208,27 +185,16 @@
 
     @SuppressWarnings("unchecked")
     // No proper solution with HashMap runtime cast
-    public void updateLists() throws RemoteException {
-        HashMap<String, Conference> confs = (HashMap<String, Conference>) mCallbacks.getService().getConferenceList();
-        Log.i(TAG, "There are " + confs.size());
-        sortConferences(confs);
-    }
-
-    private void sortConferences(HashMap<String, Conference> conferences) {
-
-        ArrayList<Conference> multiConfs = new ArrayList<Conference>();
-        ArrayList<Conference> oneToOneConfs = new ArrayList<Conference>();
-        for (Conference conf : conferences.values()) {
-            if (conf.hasMultipleParticipants())
-                multiConfs.add(conf);
-            else
-                oneToOneConfs.add(conf);
+    public void updateLists() {
+        HashMap<String, Conference> confs;
+        try {
+            confs = (HashMap<String, Conference>) mCallbacks.getService().getConferenceList();
+            nb_confs.setText("" + confs.size());
+            confs_adapter.updateDataset(new ArrayList<Conference>(confs.values()));
+        } catch (RemoteException e) {
+            e.printStackTrace();
         }
 
-        nb_confs.setText("" + multiConfs.size());
-        nb_calls.setText("" + oneToOneConfs.size());
-        confs_adapter.updateDataset(multiConfs);
-        calls_adapter.updateDataset(oneToOneConfs);
     }
 
     @Override
@@ -261,18 +227,11 @@
         Log.i(TAG, "onCreateView");
         View inflatedView = inflater.inflate(R.layout.frag_call_list, container, false);
 
-        nb_calls = (TextView) inflatedView.findViewById(R.id.calls_counter);
         nb_confs = (TextView) inflatedView.findViewById(R.id.confs_counter);
 
         confs_adapter = new CallListAdapter(getActivity());
         ((ListView) inflatedView.findViewById(R.id.confs_list)).setAdapter(confs_adapter);
-
-        calls_adapter = new CallListAdapter(getActivity());
-        ((ListView) inflatedView.findViewById(R.id.calls_list)).setAdapter(calls_adapter);
-        ((ListView) inflatedView.findViewById(R.id.calls_list)).setOnItemClickListener(callClickListener);
         ((ListView) inflatedView.findViewById(R.id.confs_list)).setOnItemClickListener(callClickListener);
-
-        ((ListView) inflatedView.findViewById(R.id.calls_list)).setOnItemLongClickListener(mItemLongClickListener);
         ((ListView) inflatedView.findViewById(R.id.confs_list)).setOnItemLongClickListener(mItemLongClickListener);
 
         return inflatedView;
@@ -282,7 +241,10 @@
 
         @Override
         public void onItemClick(AdapterView<?> arg0, View v, int arg2, long arg3) {
-            mCallbacks.selectedCall((Conference) v.getTag());
+            Intent intent = new Intent().setClass(getActivity(), CallActivity.class);
+            intent.putExtra("resuming", true);
+            intent.putExtra("conference", (Conference) v.getTag());
+            startActivityForResult(intent, HomeActivity.REQUEST_CODE_CALL);
         }
     };
 
@@ -442,7 +404,7 @@
     @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
-        Conference transfer = null;
+        Conference transfer;
         if (requestCode == REQUEST_TRANSFER) {
             switch (resultCode) {
                 case 0:
@@ -451,9 +413,9 @@
                     try {
 
                         mCallbacks.getService().attendedTransfer(transfer.getParticipants().get(0).getCallId(), c.getParticipants().get(0).getCallId());
-                        calls_adapter.remove(transfer);
-                        calls_adapter.remove(c);
-                        calls_adapter.notifyDataSetChanged();
+                        confs_adapter.remove(transfer);
+                        confs_adapter.remove(c);
+                        confs_adapter.notifyDataSetChanged();
                     } catch (RemoteException e) {
                         // TODO Auto-generated catch block
                         e.printStackTrace();
@@ -496,7 +458,7 @@
     private void bindCalls(Conference call_to_add, Conference call_target) {
         try {
 
-            Log.i(TAG, "joining calls:"+ call_to_add.getId() + " and " + call_target.getId());
+            Log.i(TAG, "joining calls:" + call_to_add.getId() + " and " + call_target.getId());
 
             if (call_target.hasMultipleParticipants() && !call_to_add.hasMultipleParticipants()) {
 
diff --git a/src/org/sflphone/history/HistoryCall.java b/src/org/sflphone/history/HistoryCall.java
index ac13f48..972a1c4 100644
--- a/src/org/sflphone/history/HistoryCall.java
+++ b/src/org/sflphone/history/HistoryCall.java
@@ -56,7 +56,7 @@
     @DatabaseField
     boolean missed;
     @DatabaseField
-    String direction;
+    int direction;
     @DatabaseField
     String recordPath;
     @DatabaseField
@@ -79,8 +79,8 @@
         call_end = call.getTimestampEnd_();
         accountID = call.getAccount().getAccountID();
         number = call.getContact().getPhones().get(0).getNumber();
-        missed = call.isRinging() || call.isIncoming();
-        direction = call.getCallTypeString();
+        missed = call.isRinging() && call.isIncoming();
+        direction = call.getCallType();
         recordPath = call.getRecordPath();
         contactID = call.getContact().getId();
         callID = call.getCallId();
@@ -91,7 +91,14 @@
     }
 
     public String getDirection() {
-        return direction;
+        switch (direction) {
+            case SipCall.direction.CALL_TYPE_INCOMING:
+                return "CALL_TYPE_INCOMING";
+            case SipCall.direction.CALL_TYPE_OUTGOING:
+                return "CALL_TYPE_OUTGOING";
+            default:
+                return "CALL_TYPE_UNDETERMINED";
+        }
     }
 
     public String getDate() {
@@ -145,7 +152,7 @@
         dest.writeString(accountID);
         dest.writeString(number);
         dest.writeByte((byte) (missed ? 1 : 0));
-        dest.writeString(direction);
+        dest.writeInt(direction);
         dest.writeString(recordPath);
         dest.writeLong(contactID);
         dest.writeString(callID);
@@ -167,7 +174,7 @@
         accountID = in.readString();
         number = in.readString();
         missed = in.readByte() == 1 ? true : false;
-        direction = in.readString();
+        direction = in.readInt();
         recordPath = in.readString();
         contactID = in.readLong();
         callID = in.readString();
@@ -178,7 +185,7 @@
     }
 
     public boolean isIncoming() {
-        return true;
+        return direction == SipCall.direction.CALL_TYPE_INCOMING;
     }
 
     public boolean isMissed() {
diff --git a/src/org/sflphone/history/HistoryManager.java b/src/org/sflphone/history/HistoryManager.java
index 70108e1..bc90274 100644
--- a/src/org/sflphone/history/HistoryManager.java
+++ b/src/org/sflphone/history/HistoryManager.java
@@ -65,6 +65,10 @@
         return true;
     }
 
+    /*
+    * Necessary when user hang up a call in a Conference
+    * The call creates an HistoryCall, but the conference still goes on
+    */
     public boolean insertNewEntry(SipCall toInsert){
         return true;
     }
diff --git a/src/org/sflphone/model/SipCall.java b/src/org/sflphone/model/SipCall.java
index a1f4a1e..1b1e6cb 100644
--- a/src/org/sflphone/model/SipCall.java
+++ b/src/org/sflphone/model/SipCall.java
@@ -72,7 +72,7 @@
         timestampEnd_ = in.readLong();
     }
 
-    public SipCall(String id, Account account, int call_type, int call_state, int media_state, CallContact c) {
+    private SipCall(String id, Account account, int call_type, int call_state, int media_state, CallContact c) {
         mCallID = id;
         mAccount = account;
         mCallType = call_type;
@@ -89,6 +89,10 @@
         return "";
     }
 
+    public int getCallType() {
+        return mCallType;
+    }
+
     public interface direction {
         public static final int CALL_TYPE_INCOMING = 1;
         public static final int CALL_TYPE_OUTGOING = 2;
@@ -314,7 +318,6 @@
     public boolean isOutGoing() {
         if (mCallType == direction.CALL_TYPE_OUTGOING)
             return true;
-
         return false;
     }
 
diff --git a/src/org/sflphone/service/CallManagerCallBack.java b/src/org/sflphone/service/CallManagerCallBack.java
index de06377..40ae6ac 100644
--- a/src/org/sflphone/service/CallManagerCallBack.java
+++ b/src/org/sflphone/service/CallManagerCallBack.java
@@ -243,43 +243,51 @@
 
     @Override
     public void on_conference_removed(String confID) {
+        Log.i(TAG, "on_conference_removed:");
         Intent intent = new Intent(CONF_REMOVED);
         intent.putExtra("confID", confID);
 
         Conference toReInsert = mService.getConferences().get(confID);
-        /*for (int i = 0; i < toDestroy.getParticipants().size(); ++i) {
-            mService.getCurrentCalls().put(toDestroy.getParticipants().get(i).getCallId(), toDestroy.getParticipants().get(i));
-        }*/
+        for (SipCall call : toReInsert.getParticipants()) {
+            mService.getConferences().put(call.getCallId(), new Conference(call));
+        }
         mService.getConferences().remove(confID);
-        mService.getConferences().put(toReInsert.getId(), toReInsert);
         mService.sendBroadcast(intent);
 
     }
 
     @Override
     public void on_conference_state_changed(String confID, String state) {
-
+        Log.i(TAG, "on_conference_state_changed:");
         Intent intent = new Intent(CONF_CHANGED);
         intent.putExtra("confID", confID);
         intent.putExtra("State", state);
+        mService.getConferences().get(confID).setCallState(confID, state);
 
-        StringVect all_participants = mService.getCallManagerJNI().getParticipantList(intent.getStringExtra("confID"));
-        for (int i = 0; i < all_participants.size(); ++i) {
-            if (mService.getConferences().get(confID).getParticipants().size() < all_participants.size()
-                    && mService.getConferences().get(all_participants.get(i)) != null) { // We need to add the new participant to the conf
-                mService.getConferences().get(confID).addParticipant(mService.getConferences().get(all_participants.get(i)).getCallById(all_participants.get(i)));
-                mService.getConferences().remove(all_participants.get(i));
-                mService.getConferences().get(confID).setCallState(confID, intent.getStringExtra("State"));
-                mService.sendBroadcast(intent);
-                return;
+        Log.i(TAG, "Received:" + intent.getAction());
+        Log.i(TAG, "State:" + state);
+
+        Conference toModify = mService.getConferences().get(confID);
+
+        ArrayList<String> newParticipants = SwigNativeConverter.convertSwigToNative(mService.getCallManagerJNI().getParticipantList(intent.getStringExtra("confID")));
+
+        if (toModify.getParticipants().size() < newParticipants.size()) {
+            // We need to add the new participant to the conf
+            for (int i = 0; i < newParticipants.size(); ++i) {
+                if(toModify.getCallById(newParticipants.get(i))==null){
+                    mService.addCallToConference(toModify.getId(), newParticipants.get(i));
+                }
+            }
+        } else if (toModify.getParticipants().size() > newParticipants.size()) {
+
+            for (SipCall participant : toModify.getParticipants()) {
+                if (!newParticipants.contains(participant.getCallId())) {
+                    mService.removeCallFromConference(toModify.getId(), participant.getCallId());
+                }
             }
         }
 
-        Log.i(TAG, "Received" + intent.getAction());
-        if (mService.getConferences().get(confID) != null) {
-            mService.getConferences().get(confID).setCallState(confID, state);
-            mService.sendBroadcast(intent);
-        }
+        mService.sendBroadcast(intent);
     }
 
     @Override
diff --git a/src/org/sflphone/service/SipService.java b/src/org/sflphone/service/SipService.java
index 78221ba..c5de94d 100644
--- a/src/org/sflphone/service/SipService.java
+++ b/src/org/sflphone/service/SipService.java
@@ -83,6 +83,33 @@
         return mConferences;
     }
 
+    public void addCallToConference(String confId, String callId) {
+        if(mConferences.get(callId) != null){
+            // We add a simple call to a conference
+            mConferences.get(confId).addParticipant(mConferences.get(callId).getParticipants().get(0));
+            mConferences.remove(callId);
+        } else {
+            Iterator<Map.Entry<String, Conference>> it = mConferences.entrySet().iterator();
+            while (it.hasNext()) {
+                Conference tmp = it.next().getValue();
+                for (SipCall c : tmp.getParticipants()) {
+                    if (c.getCallId().contentEquals(callId)) {
+                        mConferences.get(confId).addParticipant(c);
+                        mConferences.get(tmp.getId()).removeParticipant(c.getCallId());
+                    }
+                }
+            }
+        }
+
+    }
+
+    public void removeCallFromConference(String confId, String callId) {
+        SipCall call = mConferences.get(confId).getCallById(callId);
+        Conference separate = new Conference(call);
+        mConferences.put(separate.getId(), separate);
+    }
+
+
     @Override
     public boolean onUnbind(Intent i) {
         super.onUnbind(i);
@@ -125,7 +152,7 @@
         Log.i(TAG, "onDestroy");
         /* called once by stopService() */
         mNotificationManager.onServiceDestroy();
-
+        mMediaManager.stopService();
         getExecutor().execute(new FinalizeRunnable());
         super.onDestroy();
 
@@ -358,7 +385,8 @@
                 protected void doRun() throws SameThreadException {
                     Log.i(TAG, "SipService.hangUp() thread running...");
                     callManagerJNI.hangUp(callID);
-                    mMediaManager.abandonAudioFocus();
+                    if(mConferences.size() == 0)
+                        mMediaManager.abandonAudioFocus();
                 }
             });
         }
diff --git a/src/org/sflphone/utils/SwigNativeConverter.java b/src/org/sflphone/utils/SwigNativeConverter.java
index aa6fce5..306d580 100644
--- a/src/org/sflphone/utils/SwigNativeConverter.java
+++ b/src/org/sflphone/utils/SwigNativeConverter.java
@@ -42,6 +42,7 @@
 import org.sflphone.account.AccountDetailTls;
 import org.sflphone.service.ServiceConstants;
 import org.sflphone.service.StringMap;
+import org.sflphone.service.StringVect;
 import org.sflphone.service.VectMap;
 
 public class SwigNativeConverter {
@@ -179,4 +180,11 @@
         return toReturn;
     }
 
+    public static ArrayList<String> convertSwigToNative(StringVect vector) {
+        ArrayList<String> toReturn = new ArrayList<String>();
+        for (int i = 0; i < vector.size(); ++i) {
+            toReturn.add(vector.get(i));
+        }
+        return toReturn;
+    }
 }