create a conversation fragment

In the futur, we want to integrate the current conversation in the
home page. To do this, it is necessary to create a conversation
fragment.

The conversation activity calls the conversation fragment. The
activity manages the LocalService and the fragment manages the
conversation management.

Change-Id: If37d76840b0ada744d9da811092d23a5351a3847
Tuleap: #1431
diff --git a/ring-android/app/src/main/AndroidManifest.xml b/ring-android/app/src/main/AndroidManifest.xml
index 17de2e5..3c2e941 100644
--- a/ring-android/app/src/main/AndroidManifest.xml
+++ b/ring-android/app/src/main/AndroidManifest.xml
@@ -231,7 +231,7 @@
         </activity>
         <activity
             android:name=".client.ConversationActivity"
-            android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize"
+            android:configChanges="screenSize|screenLayout|smallestScreenSize"
             android:label="@string/app_name"
             android:parentActivityName=".client.HomeActivity"
             android:screenOrientation="fullUser"
diff --git a/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java b/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java
index d8cb273..85f0d23 100644
--- a/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java
@@ -27,284 +27,39 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
-import android.graphics.Bitmap;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.SystemClock;
-import android.support.design.widget.Snackbar;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AlertDialog;
 import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.DefaultItemAnimator;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.Toolbar;
 import android.util.Log;
-import android.util.Pair;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.inputmethod.EditorInfo;
-import android.widget.EditText;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-import java.util.List;
-
-import javax.inject.Inject;
 
 import butterknife.BindView;
 import butterknife.ButterKnife;
-import butterknife.OnClick;
-import butterknife.OnEditorAction;
 import cx.ring.R;
-import cx.ring.adapters.ContactDetailsTask;
-import cx.ring.adapters.ConversationAdapter;
-import cx.ring.adapters.NumberAdapter;
-import cx.ring.application.RingApplication;
-import cx.ring.model.Account;
-import cx.ring.model.CallContact;
-import cx.ring.model.Conference;
-import cx.ring.model.Conversation;
-import cx.ring.model.Phone;
-import cx.ring.model.Uri;
+import cx.ring.fragments.ConversationFragment;
 import cx.ring.service.LocalService;
-import cx.ring.services.AccountService;
-import cx.ring.services.CallService;
-import cx.ring.utils.ActionHelper;
-import cx.ring.utils.ClipboardHelper;
-import cx.ring.utils.ContentUriHandler;
 
-public class ConversationActivity extends AppCompatActivity implements
-        Conversation.ConversationActionCallback,
-        ClipboardHelper.ClipboardHelperCallback,
-        ContactDetailsTask.DetailsLoadedCallback {
-
-    @Inject
-    CallService mCallService;
-
-    @Inject
-    AccountService mAccountService;
+public class ConversationActivity extends AppCompatActivity {
 
     @BindView(R.id.main_toolbar)
     Toolbar mToolbar;
 
-    @BindView(R.id.msg_input_txt)
-    EditText mMsgEditTxt;
-
-    @BindView(R.id.msg_send)
-    View mMsgSendBtn;
-
-    @BindView(R.id.ongoingcall_pane)
-    ViewGroup mBottomPane;
-
-    @BindView(R.id.hist_list)
-    RecyclerView mHistList;
-
-    @BindView(R.id.number_selector)
-    Spinner mNumberSpinner;
-
     private static final String TAG = ConversationActivity.class.getSimpleName();
-    private static final String CONVERSATION_DELETE = "CONVERSATION_DELETE";
-
-    public static final int REQ_ADD_CONTACT = 42;
     static final long REFRESH_INTERVAL_MS = 30 * 1000;
 
     private boolean mBound = false;
-    private boolean mVisible = false;
-    private AlertDialog mDeleteDialog;
-    private boolean mDeleteConversation = false;
-
     private LocalService mService = null;
-    private Conversation mConversation = null;
-    private Uri mPreferredNumber = null;
-
-    private MenuItem mAddContactBtn = null;
-
-    private ConversationAdapter mAdapter = null;
-    private NumberAdapter mNumberAdapter = null;
-
     private final Handler mRefreshTaskHandler = new Handler();
-
-    static private Pair<Conversation, Uri> getConversation(LocalService s, Intent i) {
-        if (s == null || i == null || i.getData() == null)
-            return new Pair<>(null, null);
-
-        String conv_id = i.getData().getLastPathSegment();
-        Uri number = new Uri(i.getStringExtra("number"));
-
-        Log.d(TAG, "getConversation " + conv_id + " " + number);
-        Conversation conv = s.getConversation(conv_id);
-        if (conv == null) {
-            long contact_id = CallContact.contactIdFromId(conv_id);
-            Log.d(TAG, "no conversation found, contact_id " + contact_id);
-            CallContact contact = null;
-            if (contact_id >= 0)
-                contact = s.findContactById(contact_id);
-            if (contact == null) {
-                Uri conv_uri = new Uri(conv_id);
-                if (!number.isEmpty()) {
-                    contact = s.findContactByNumber(number);
-                    if (contact == null)
-                        contact = CallContact.buildUnknown(conv_uri);
-                } else {
-                    contact = s.findContactByNumber(conv_uri);
-                    if (contact == null) {
-                        contact = CallContact.buildUnknown(conv_uri);
-                        number = contact.getPhones().get(0).getNumber();
-                    } else {
-                        number = conv_uri;
-                    }
-                }
-            }
-            conv = s.startConversation(contact);
-        }
-
-        Log.d(TAG, "returning " + conv.getContact().getDisplayName() + " " + number);
-        return new Pair<>(conv, number);
-    }
-
-    static private int getIndex(Spinner spinner, Uri myString) {
-        for (int i = 0, n = spinner.getCount(); i < n; i++)
-            if (((Phone) spinner.getItemAtPosition(i)).getNumber().equals(myString))
-                return i;
-        return 0;
-    }
+    private ConversationFragment mConversationFragment;
 
     @Override
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
-        refreshView(0);
+        mConversationFragment.refreshView(0);
     }
 
-    private void refreshView(long refreshed) {
-        Pair<Conversation, Uri> conv = getConversation(mService, getIntent());
-        mConversation = conv.first;
-        mPreferredNumber = conv.second;
-
-        if (mConversation == null) {
-            finish();
-            return;
-        }
-
-        ActionBar ab = getSupportActionBar();
-        if (ab != null) {
-            if (!mConversation.getContact().getPhones().isEmpty()) {
-                CallContact contact = mCallService.getContact(mConversation.getContact().getPhones().get(0).getNumber());
-                if (contact != null) {
-                    mConversation.setContact(contact);
-                }
-            }
-
-            ab.setTitle(mConversation.getContact().getDisplayName());
-        }
-
-        final CallContact contact = mConversation.getContact();
-        if (contact != null) {
-            new ContactDetailsTask(this, contact, this).run();
-        }
-
-        Conference conf = mConversation.getCurrentCall();
-        mBottomPane.setVisibility(conf == null ? View.GONE : View.VISIBLE);
-        if (conf != null) {
-            Log.d(TAG, "ConversationActivity refreshView " + conf.getId() + " "
-                    + mConversation.getCurrentCall());
-        }
-
-        mAdapter.updateDataset(mConversation.getAggregateHistory(), refreshed);
-
-        if (mConversation.getContact().getPhones().size() > 1) {
-            mNumberSpinner.setVisibility(View.VISIBLE);
-            mNumberAdapter = new NumberAdapter(ConversationActivity.this,
-                    mConversation.getContact(),
-                    false);
-            mNumberSpinner.setAdapter(mNumberAdapter);
-            if (mPreferredNumber == null || mPreferredNumber.isEmpty()) {
-                mPreferredNumber = new Uri(
-                        mConversation.getLastNumberUsed(mConversation.getLastAccountUsed())
-                );
-            }
-            mNumberSpinner.setSelection(getIndex(mNumberSpinner, mPreferredNumber));
-        } else {
-            mNumberSpinner.setVisibility(View.GONE);
-            mPreferredNumber = mConversation.getContact().getPhones().get(0).getNumber();
-        }
-
-        invalidateOptionsMenu();
-    }
-
-    private ServiceConnection mConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName className, IBinder binder) {
-            Log.d(TAG, "ConversationActivity onServiceConnected " + className.getClassName());
-            mService = ((LocalService.LocalBinder) binder).getService();
-
-            mAdapter = new ConversationAdapter(ConversationActivity.this,
-                    mService.get40dpContactCache(),
-                    mService.getThreadPool());
-
-            if (mHistList != null) {
-                mHistList.setAdapter(mAdapter);
-            }
-
-            refreshView(0);
-
-            IntentFilter filter = new IntentFilter(LocalService.ACTION_CONF_UPDATE);
-            registerReceiver(receiver, filter);
-
-            mBound = true;
-            if (mVisible && mConversation != null && !mConversation.isVisible()) {
-                mConversation.setVisible(true);
-                mService.readConversation(mConversation);
-            }
-
-            if (mDeleteConversation) {
-                mDeleteDialog = ActionHelper.launchDeleteAction(ConversationActivity.this, mConversation, ConversationActivity.this);
-            }
-
-            mRefreshTaskHandler.postDelayed(refreshTask, REFRESH_INTERVAL_MS);
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName arg0) {
-            Log.d(TAG, "ConversationActivity onServiceDisconnected " + arg0.getClassName());
-            mBound = false;
-            mRefreshTaskHandler.removeCallbacks(refreshTask);
-            if (mConversation != null) {
-                mConversation.setVisible(false);
-            }
-        }
-    };
-
-    private final BroadcastReceiver receiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            Log.d(TAG, "onReceive " + intent.getAction() + " " + intent.getDataString());
-            refreshView(intent.getLongExtra(LocalService.ACTION_CONF_UPDATE_EXTRA_MSG, 0));
-            if (mAdapter.getItemCount() > 0)
-                mHistList.smoothScrollToPosition(mAdapter.getItemCount() - 1);
-        }
-    };
-
-    private final Runnable refreshTask = new Runnable() {
-        private long lastRefresh = 0;
-
-        public void run() {
-            if (lastRefresh == 0)
-                lastRefresh = SystemClock.uptimeMillis();
-            else
-                lastRefresh += REFRESH_INTERVAL_MS;
-
-            mAdapter.notifyDataSetChanged();
-
-            mRefreshTaskHandler.postAtTime(this, lastRefresh + REFRESH_INTERVAL_MS);
-        }
-    };
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -315,25 +70,13 @@
 
         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
 
-        // Dependency injection
-        ((RingApplication) getApplication()).getRingInjectionComponent().inject(this);
-
-        if (mBottomPane != null) {
-            mBottomPane.setVisibility(View.GONE);
+        if (mConversationFragment == null) {
+            mConversationFragment = new ConversationFragment();
+            getFragmentManager().beginTransaction()
+                    .replace(R.id.main_frame, mConversationFragment, null)
+                    .commit();
         }
 
-        LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
-        mLayoutManager.setStackFromEnd(true);
-
-        if (mHistList != null) {
-            mHistList.setLayoutManager(mLayoutManager);
-            mHistList.setAdapter(mAdapter);
-            mHistList.setItemAnimator(new DefaultItemAnimator());
-        }
-
-        // reload delete conversation state (before rotation)
-        mDeleteConversation = savedInstanceState != null && savedInstanceState.getBoolean(CONVERSATION_DELETE);
-
         if (!mBound) {
             Log.d(TAG, "onCreate: Binding service...");
             Intent intent = new Intent(this, LocalService.class);
@@ -342,243 +85,80 @@
         }
     }
 
-    @OnClick(R.id.msg_send)
-    public void sendMessageText(View sender){
-        CharSequence txt = mMsgEditTxt.getText();
-        if (txt.length() > 0) {
-            onSendTextMessage(txt.toString());
-            mMsgEditTxt.setText("");
-        }
-    }
+    private ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName className, IBinder binder) {
+            Log.d(TAG, "ConversationActivity onServiceConnected " + className.getClassName());
+            mService = ((LocalService.LocalBinder) binder).getService();
 
-    @OnEditorAction(R.id.msg_input_txt)
-    public boolean actionSendMsgText(TextView view, int actionId, KeyEvent event){
-        switch (actionId) {
-            case EditorInfo.IME_ACTION_SEND:
-                CharSequence txt = mMsgEditTxt.getText();
-                if (txt.length() > 0) {
-                    onSendTextMessage(mMsgEditTxt.getText().toString());
-                    mMsgEditTxt.setText("");
-                }
-                return true;
-        }
-        return false;
-    }
+            IntentFilter filter = new IntentFilter(LocalService.ACTION_CONF_UPDATE);
+            registerReceiver(receiver, filter);
 
-    @OnClick(R.id.ongoingcall_pane)
-    public void onClick(View v) {
-        startActivity(new Intent(Intent.ACTION_VIEW)
-                .setClass(getApplicationContext(), CallActivity.class)
-                .setData(android.net.Uri.withAppendedPath(ContentUriHandler.CONFERENCE_CONTENT_URI,
-                        mConversation.getCurrentCall().getId())));
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        Log.d(TAG, "onPause");
-        mVisible = false;
-        if (mConversation != null) {
-            mService.readConversation(mConversation);
-            mConversation.setVisible(false);
-        }
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        Log.d(TAG, "onResume " + mConversation);
-        mVisible = true;
-        if (mConversation != null) {
-            mConversation.setVisible(true);
-            if (mBound && mService != null) {
-                mService.readConversation(mConversation);
+            if (mConversationFragment != null) {
+                mConversationFragment.setCallback(mService);
+                mConversationFragment.refreshView(0);
             }
+
+            mBound = true;
+
+            mRefreshTaskHandler.postDelayed(refreshTask, REFRESH_INTERVAL_MS);
         }
-    }
+
+        @Override
+        public void onServiceDisconnected(ComponentName arg0) {
+            Log.d(TAG, "ConversationActivity onServiceDisconnected " + arg0.getClassName());
+            mBound = false;
+            mRefreshTaskHandler.removeCallbacks(refreshTask);
+        }
+    };
+
+    private final Runnable refreshTask = new Runnable() {
+        private long lastRefresh = 0;
+
+        public void run() {
+            if (lastRefresh == 0) {
+                lastRefresh = SystemClock.uptimeMillis();
+            } else {
+                lastRefresh += REFRESH_INTERVAL_MS;
+            }
+
+            mRefreshTaskHandler.postAtTime(this, lastRefresh + REFRESH_INTERVAL_MS);
+        }
+    };
+
+    private final BroadcastReceiver receiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.d(TAG, "onReceive " + intent.getAction() + " " + intent.getDataString());
+            mConversationFragment.refreshView(intent.getLongExtra(LocalService.ACTION_CONF_UPDATE_EXTRA_MSG, 0));
+        }
+    };
 
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
         switch (requestCode) {
-            case REQ_ADD_CONTACT:
-                if (mService != null) mService.refreshConversations();
+            case ConversationFragment.REQ_ADD_CONTACT:
+                if (mService != null) {
+                    mService.refreshConversations();
+                }
                 break;
         }
     }
 
     @Override
-    protected void onDestroy() {
-        if (mBound) {
-            unregisterReceiver(receiver);
-            unbindService(mConnection);
-            mBound = false;
-        }
-
-        if (mDeleteConversation) {
-            mDeleteDialog.dismiss();
-        }
-
-        super.onDestroy();
-    }
-
-    @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-
-        // persist the delete popup state in case of Activity rotation
-        mDeleteConversation = mDeleteDialog != null && mDeleteDialog.isShowing();
-        outState.putBoolean(CONVERSATION_DELETE, mDeleteConversation);
-    }
-
-    @Override
-    public boolean onPrepareOptionsMenu(Menu menu) {
-        if (mAddContactBtn != null) {
-            mAddContactBtn.setVisible(mConversation != null && mConversation.getContact().getId() < 0);
-        }
-        return super.onPrepareOptionsMenu(menu);
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        MenuInflater inflater = getMenuInflater();
-        inflater.inflate(R.menu.conversation_actions, menu);
-        mAddContactBtn = menu.findItem(R.id.menuitem_addcontact);
-        return super.onCreateOptionsMenu(menu);
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case android.R.id.home:
-                startActivity(new Intent(this, HomeActivity.class));
-                finish();
-                return true;
-            case R.id.conv_action_audiocall:
-                onCallWithVideo(false);
-                return true;
-            case R.id.conv_action_videocall:
-                onCallWithVideo(true);
-                return true;
-            case R.id.menuitem_addcontact:
-                startActivityForResult(ActionHelper.getAddNumberIntentForContact(mConversation.getContact()), REQ_ADD_CONTACT);
-                return true;
-            case R.id.menuitem_delete:
-                mDeleteDialog = ActionHelper.launchDeleteAction(this, this.mConversation, this);
-                return true;
-            case R.id.menuitem_copy_content:
-                ActionHelper.launchCopyNumberToClipboardFromContact(this,
-                        this.mConversation.getContact(), this);
-                return true;
-            default:
-                return super.onOptionsItemSelected(item);
-        }
-    }
-
-    /**
-     * Guess account and number to use to initiate a call
-     */
-    private Pair<Account, Uri> guess() {
-        Uri number = mNumberAdapter == null ?
-                mPreferredNumber : ((Phone) mNumberSpinner.getSelectedItem()).getNumber();
-        Account a = mAccountService.getAccount(mConversation.getLastAccountUsed());
-
-        // Guess account from number
-        if (a == null && number != null)
-            a = mAccountService.guessAccount(number);
-
-        // Guess number from account/call history
-        if (a != null && (number == null/* || number.isEmpty()*/))
-            number = new Uri(mConversation.getLastNumberUsed(a.getAccountID()));
-
-        // If no account found, use first active
-        if (a == null) {
-            List<Account> accs = mAccountService.getAccounts();
-            if (accs.isEmpty()) {
-                finish();
-                return null;
-            } else
-                a = accs.get(0);
-        }
-
-        // If no number found, use first from contact
-        if (number == null || number.isEmpty())
-            number = mConversation.getContact().getPhones().get(0).getNumber();
-
-        return new Pair<>(a, number);
-    }
-
-    private void onSendTextMessage(String txt) {
-        Conference conf = mConversation == null ? null : mConversation.getCurrentCall();
-        if (conf == null || !conf.isOnGoing()) {
-            Pair<Account, Uri> g = guess();
-            if (g == null || g.first == null)
-                return;
-            mService.sendTextMessage(g.first.getAccountID(), g.second, txt);
-        } else {
-            mService.sendTextMessage(conf, txt);
-        }
-    }
-
-    private void onCallWithVideo(boolean has_video) {
-        Conference conf = mConversation.getCurrentCall();
-        if (conf != null) {
-            startActivity(new Intent(Intent.ACTION_VIEW)
-                    .setClass(getApplicationContext(), CallActivity.class)
-                    .setData(android.net.Uri.withAppendedPath(ContentUriHandler.CONFERENCE_CONTENT_URI, conf.getId())));
-            return;
-        }
-        Pair<Account, Uri> g = guess();
-        if (g == null || g.first == null)
-            return;
-
-        try {
-            Intent intent = new Intent(CallActivity.ACTION_CALL)
-                    .setClass(getApplicationContext(), CallActivity.class)
-                    .putExtra("account", g.first.getAccountID())
-                    .putExtra("video", has_video)
-                    .setData(android.net.Uri.parse(g.second.getRawUriString()));
-            startActivityForResult(intent, HomeActivity.REQUEST_CODE_CALL);
-        } catch (Exception e) {
-            e.printStackTrace();
-            Log.e(TAG, e.toString());
-        }
-    }
-
-    @Override
-    public void deleteConversation(Conversation conversation) {
-        if (mService != null) {
-            mService.deleteConversation(conversation);
-        }
-        finish();
-    }
-
-    @Override
-    public void copyContactNumberToClipboard(String contactNumber) {
-        ClipboardHelper.copyNumberToClipboard(this, contactNumber, this);
-    }
-
-    @Override
-    public void clipBoardDidCopyNumber(String copiedNumber) {
-        View view = this.findViewById(android.R.id.content);
-        if (view != null) {
-            String snackbarText = getString(R.string.conversation_action_copied_peer_number_clipboard,
-                    Phone.getShortenedNumber(copiedNumber));
-            Snackbar.make(view, snackbarText, Snackbar.LENGTH_LONG).show();
-        }
-    }
-
-    @Override
-    public void onDetailsLoaded(Bitmap bmp, String formattedName) {
-        ActionBar ab = getSupportActionBar();
-        if (ab != null && formattedName != null) {
-            ab.setTitle(formattedName);
-        }
-    }
-
-    @Override
     public void onBackPressed() {
         startActivity(new Intent(this, HomeActivity.class));
         finish();
     }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mBound) {
+            unregisterReceiver(receiver);
+            unbindService(mConnection);
+            mBound = false;
+        }
+    }
 }
diff --git a/ring-android/app/src/main/java/cx/ring/dependencyinjection/RingInjectionComponent.java b/ring-android/app/src/main/java/cx/ring/dependencyinjection/RingInjectionComponent.java
index 0577fda..f31dccb 100755
--- a/ring-android/app/src/main/java/cx/ring/dependencyinjection/RingInjectionComponent.java
+++ b/ring-android/app/src/main/java/cx/ring/dependencyinjection/RingInjectionComponent.java
@@ -27,12 +27,12 @@
 import cx.ring.client.AccountEditionActivity;
 import cx.ring.client.AccountWizard;
 import cx.ring.client.CallActivity;
-import cx.ring.client.ConversationActivity;
 import cx.ring.client.HomeActivity;
 import cx.ring.fragments.AccountCreationFragment;
 import cx.ring.fragments.AccountMigrationFragment;
 import cx.ring.fragments.AccountsManagementFragment;
 import cx.ring.fragments.CallFragment;
+import cx.ring.fragments.ConversationFragment;
 import cx.ring.fragments.DeviceAccountFragment;
 import cx.ring.fragments.MediaPreferenceFragment;
 import cx.ring.fragments.ProfileCreationFragment;
@@ -69,8 +69,6 @@
 
     void inject(CallActivity activity);
 
-    void inject(ConversationActivity activity);
-
     void inject(HomeActivity activity);
 
     void inject(AccountWizard activity);
@@ -105,6 +103,8 @@
 
     void inject(RegisterNameDialog dialog);
 
+    void inject(ConversationFragment fragment);
+
     void inject(LocalService service);
 
     void inject(DRingService service);
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java
index 6266f61..1d2010c 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java
@@ -452,7 +452,7 @@
                     break;
                 }
                 startActivityForResult(ActionHelper.getAddNumberIntentForContact(firstParticipant.getContact()),
-                        ConversationActivity.REQ_ADD_CONTACT);
+                        ConversationFragment.REQ_ADD_CONTACT);
                 break;
             case R.id.menuitem_speaker:
                 audioManager.setSpeakerphoneOn(!audioManager.isSpeakerphoneOn());
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java
new file mode 100644
index 0000000..b6e721d
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java
@@ -0,0 +1,478 @@
+package cx.ring.fragments;
+
+import android.app.Fragment;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.support.design.widget.Snackbar;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.widget.DefaultItemAnimator;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.util.Pair;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
+import butterknife.OnEditorAction;
+import cx.ring.R;
+import cx.ring.adapters.ContactDetailsTask;
+import cx.ring.adapters.ConversationAdapter;
+import cx.ring.adapters.NumberAdapter;
+import cx.ring.application.RingApplication;
+import cx.ring.client.CallActivity;
+import cx.ring.client.ConversationActivity;
+import cx.ring.client.HomeActivity;
+import cx.ring.model.Account;
+import cx.ring.model.CallContact;
+import cx.ring.model.Conference;
+import cx.ring.model.Conversation;
+import cx.ring.model.Phone;
+import cx.ring.model.Uri;
+import cx.ring.service.LocalService;
+import cx.ring.services.AccountService;
+import cx.ring.services.CallService;
+import cx.ring.utils.ActionHelper;
+import cx.ring.utils.ClipboardHelper;
+import cx.ring.utils.ContentUriHandler;
+
+public class ConversationFragment extends Fragment implements
+        Conversation.ConversationActionCallback,
+        ClipboardHelper.ClipboardHelperCallback,
+        ContactDetailsTask.DetailsLoadedCallback {
+
+    @Inject
+    CallService mCallService;
+
+    @Inject
+    AccountService mAccountService;
+
+    @BindView(R.id.msg_input_txt)
+    EditText mMsgEditTxt;
+
+    @BindView(R.id.ongoingcall_pane)
+    ViewGroup mBottomPane;
+
+    @BindView(R.id.hist_list)
+    RecyclerView mHistList;
+
+    @BindView(R.id.number_selector)
+    Spinner mNumberSpinner;
+
+    private static final String TAG = ConversationFragment.class.getSimpleName();
+    private static final String CONVERSATION_DELETE = "CONVERSATION_DELETE";
+
+    public static final int REQ_ADD_CONTACT = 42;
+
+    private boolean mVisible = false;
+    private AlertDialog mDeleteDialog;
+    private boolean mDeleteConversation = false;
+
+    private LocalService mService = null;
+    private Conversation mConversation = null;
+    private Uri mPreferredNumber = null;
+
+    private MenuItem mAddContactBtn = null;
+
+    private ConversationAdapter mAdapter = null;
+    private NumberAdapter mNumberAdapter = null;
+
+    private static Pair<Conversation, Uri> getConversation(LocalService service, Intent intent) {
+        if (service == null || intent == null || intent.getData() == null) {
+            return new Pair<>(null, null);
+        }
+
+        String conversationId = intent.getData().getLastPathSegment();
+        Uri number = new Uri(intent.getStringExtra("number"));
+
+        Log.d(TAG, "getConversation " + conversationId + " " + number);
+        Conversation conversation = service.getConversation(conversationId);
+        if (conversation == null) {
+            long contactId = CallContact.contactIdFromId(conversationId);
+            Log.d(TAG, "no conversation found, contact_id " + contactId);
+            CallContact contact = null;
+            if (contactId >= 0) {
+                contact = service.findContactById(contactId);
+            }
+            if (contact == null) {
+                Uri convUri = new Uri(conversationId);
+                if (!number.isEmpty()) {
+                    contact = service.findContactByNumber(number);
+                    if (contact == null) {
+                        contact = CallContact.buildUnknown(convUri);
+                    }
+                } else {
+                    contact = service.findContactByNumber(convUri);
+                    if (contact == null) {
+                        contact = CallContact.buildUnknown(convUri);
+                        number = contact.getPhones().get(0).getNumber();
+                    } else {
+                        number = convUri;
+                    }
+                }
+            }
+            conversation = service.startConversation(contact);
+        }
+
+        Log.d(TAG, "returning " + conversation.getContact().getDisplayName() + " " + number);
+        return new Pair<>(conversation, number);
+    }
+
+    private static int getIndex(Spinner spinner, Uri myString) {
+        for (int i = 0, n = spinner.getCount(); i < n; i++)
+            if (((Phone) spinner.getItemAtPosition(i)).getNumber().equals(myString)) {
+                return i;
+            }
+        return 0;
+    }
+
+    public void refreshView(long refreshed) {
+        if (mService == null) {
+            return;
+        }
+        Pair<Conversation, Uri> conversation = getConversation(mService, getActivity().getIntent());
+        mConversation = conversation.first;
+        mPreferredNumber = conversation.second;
+
+        if (mConversation == null) {
+            return;
+        }
+
+        if (!mConversation.getContact().getPhones().isEmpty()) {
+            CallContact contact = mCallService.getContact(mConversation.getContact().getPhones().get(0).getNumber());
+            if (contact != null) {
+                mConversation.setContact(contact);
+            }
+            if (((ConversationActivity) getActivity()).getSupportActionBar() != null) {
+                ((ConversationActivity) getActivity()).getSupportActionBar().setTitle(mConversation.getContact().getDisplayName());
+            }
+        }
+
+        final CallContact contact = mConversation.getContact();
+        if (contact != null) {
+            new ContactDetailsTask(getActivity(), contact, this).run();
+        }
+
+        Conference conference = mConversation.getCurrentCall();
+        mBottomPane.setVisibility(conference == null ? View.GONE : View.VISIBLE);
+        if (conference != null) {
+            Log.d(TAG, "ConversationFragment refreshView " + conference.getId() + " "
+                    + mConversation.getCurrentCall());
+        }
+
+        mAdapter.updateDataset(mConversation.getAggregateHistory(), refreshed);
+
+        if (mConversation.getContact().getPhones().size() > 1) {
+            mNumberSpinner.setVisibility(View.VISIBLE);
+            mNumberAdapter = new NumberAdapter(getActivity(),
+                    mConversation.getContact(),
+                    false);
+            mNumberSpinner.setAdapter(mNumberAdapter);
+            if (mPreferredNumber == null || mPreferredNumber.isEmpty()) {
+                mPreferredNumber = new Uri(
+                        mConversation.getLastNumberUsed(mConversation.getLastAccountUsed())
+                );
+            }
+            mNumberSpinner.setSelection(getIndex(mNumberSpinner, mPreferredNumber));
+        } else {
+            mNumberSpinner.setVisibility(View.GONE);
+            mPreferredNumber = mConversation.getContact().getPhones().get(0).getNumber();
+        }
+
+        if (mAdapter.getItemCount() > 0) {
+            mHistList.smoothScrollToPosition(mAdapter.getItemCount() - 1);
+        }
+
+        getActivity().invalidateOptionsMenu();
+    }
+
+    public void setCallback(LocalService callback) {
+        mService = callback;
+
+        mAdapter = new ConversationAdapter(getActivity(),
+                mService.get40dpContactCache(),
+                mService.getThreadPool());
+
+        if (mHistList != null) {
+            mHistList.setAdapter(mAdapter);
+        }
+
+        if (mVisible && mConversation != null && !mConversation.isVisible()) {
+            mConversation.setVisible(true);
+            mService.readConversation(mConversation);
+        }
+
+        if (mDeleteConversation) {
+            mDeleteDialog = ActionHelper.launchDeleteAction(getActivity(), mConversation, this);
+        }
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        super.onCreateView(inflater, container, savedInstanceState);
+
+        View inflatedView = inflater.inflate(R.layout.frag_conversation, container, false);
+
+        ButterKnife.bind(this, inflatedView);
+
+        // Dependency injection
+        ((RingApplication) getActivity().getApplication()).getRingInjectionComponent().inject(this);
+
+        if (mBottomPane != null) {
+            mBottomPane.setVisibility(View.GONE);
+        }
+
+        LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
+        mLayoutManager.setStackFromEnd(true);
+
+        if (mHistList != null) {
+            mHistList.setLayoutManager(mLayoutManager);
+            mHistList.setAdapter(mAdapter);
+            mHistList.setItemAnimator(new DefaultItemAnimator());
+        }
+
+        // reload delete conversation state (before rotation)
+        mDeleteConversation = savedInstanceState != null && savedInstanceState.getBoolean(CONVERSATION_DELETE);
+
+        setHasOptionsMenu(true);
+        return inflatedView;
+    }
+
+    @OnClick(R.id.msg_send)
+    public void sendMessageText(View sender) {
+        CharSequence txt = mMsgEditTxt.getText();
+        if (txt.length() > 0) {
+            onSendTextMessage(txt.toString());
+            mMsgEditTxt.setText("");
+        }
+    }
+
+    @OnEditorAction(R.id.msg_input_txt)
+    public boolean actionSendMsgText(TextView view, int actionId, KeyEvent event) {
+        switch (actionId) {
+            case EditorInfo.IME_ACTION_SEND:
+                CharSequence txt = mMsgEditTxt.getText();
+                if (txt.length() > 0) {
+                    onSendTextMessage(mMsgEditTxt.getText().toString());
+                    mMsgEditTxt.setText("");
+                }
+                return true;
+        }
+        return false;
+    }
+
+    @OnClick(R.id.ongoingcall_pane)
+    public void onClick(View view) {
+        startActivity(new Intent(Intent.ACTION_VIEW)
+                .setClass(getActivity().getApplicationContext(), CallActivity.class)
+                .setData(android.net.Uri.withAppendedPath(ContentUriHandler.CONFERENCE_CONTENT_URI,
+                        mConversation.getCurrentCall().getId())));
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        Log.d(TAG, "onPause");
+        mVisible = false;
+        if (mConversation != null) {
+            mService.readConversation(mConversation);
+            mConversation.setVisible(false);
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        Log.d(TAG, "onResume " + mConversation);
+        mVisible = true;
+        if (mConversation != null) {
+            mConversation.setVisible(true);
+            if (mService != null) {
+                mService.readConversation(mConversation);
+            }
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        if (mDeleteConversation) {
+            mDeleteDialog.dismiss();
+        }
+
+        super.onDestroy();
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        // persist the delete popup state in case of Activity rotation
+        mDeleteConversation = mDeleteDialog != null && mDeleteDialog.isShowing();
+        outState.putBoolean(CONVERSATION_DELETE, mDeleteConversation);
+    }
+
+    @Override
+    public void onPrepareOptionsMenu(Menu menu) {
+        super.onPrepareOptionsMenu(menu);
+        if (mAddContactBtn != null) {
+            mAddContactBtn.setVisible(mConversation != null && mConversation.getContact().getId() < 0);
+        }
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        inflater.inflate(R.menu.conversation_actions, menu);
+        mAddContactBtn = menu.findItem(R.id.menuitem_addcontact);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                startActivity(new Intent(getActivity(), HomeActivity.class));
+                return true;
+            case R.id.conv_action_audiocall:
+                onCallWithVideo(false);
+                return true;
+            case R.id.conv_action_videocall:
+                onCallWithVideo(true);
+                return true;
+            case R.id.menuitem_addcontact:
+                startActivityForResult(ActionHelper.getAddNumberIntentForContact(mConversation.getContact()), REQ_ADD_CONTACT);
+                return true;
+            case R.id.menuitem_delete:
+                mDeleteDialog = ActionHelper.launchDeleteAction(getActivity(),
+                        this.mConversation,
+                        this);
+                return true;
+            case R.id.menuitem_copy_content:
+                ActionHelper.launchCopyNumberToClipboardFromContact(getActivity(),
+                        this.mConversation.getContact(),
+                        this);
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+
+    /**
+     * Guess account and number to use to initiate a call
+     */
+    private Pair<Account, Uri> guess() {
+        Uri number = mNumberAdapter == null ?
+                mPreferredNumber : ((Phone) mNumberSpinner.getSelectedItem()).getNumber();
+        Account account = mAccountService.getAccount(mConversation.getLastAccountUsed());
+
+        // Guess account from number
+        if (account == null && number != null) {
+            account = mAccountService.guessAccount(number);
+        }
+
+        // Guess number from account/call history
+        if (account != null && number == null) {
+            number = new Uri(mConversation.getLastNumberUsed(account.getAccountID()));
+        }
+
+        // If no account found, use first active
+        if (account == null) {
+            List<Account> accounts = mAccountService.getAccounts();
+            if (accounts.isEmpty()) {
+                return null;
+            } else
+                account = accounts.get(0);
+        }
+
+        // If no number found, use first from contact
+        if (number == null || number.isEmpty()) {
+            number = mConversation.getContact().getPhones().get(0).getNumber();
+        }
+
+        return new Pair<>(account, number);
+    }
+
+    private void onCallWithVideo(boolean has_video) {
+        Conference conf = mConversation.getCurrentCall();
+        if (conf != null) {
+            startActivity(new Intent(Intent.ACTION_VIEW)
+                    .setClass(getActivity().getApplicationContext(), CallActivity.class)
+                    .setData(android.net.Uri.withAppendedPath(ContentUriHandler.CONFERENCE_CONTENT_URI, conf.getId())));
+            return;
+        }
+        Pair<Account, Uri> guess = guess();
+        if (guess == null || guess.first == null) {
+            return;
+        }
+
+        try {
+            Intent intent = new Intent(CallActivity.ACTION_CALL)
+                    .setClass(getActivity().getApplicationContext(), CallActivity.class)
+                    .putExtra("account", guess.first.getAccountID())
+                    .putExtra("video", has_video)
+                    .setData(android.net.Uri.parse(guess.second.getRawUriString()));
+            startActivityForResult(intent, HomeActivity.REQUEST_CODE_CALL);
+        } catch (Exception e) {
+            Log.e(TAG, "Error during call", e);
+        }
+    }
+
+    private void onSendTextMessage(String txt) {
+        Conference conference = mConversation == null ? null : mConversation.getCurrentCall();
+        if (conference == null || !conference.isOnGoing()) {
+            Pair<Account, Uri> guess = guess();
+            if (guess == null || guess.first == null) {
+                return;
+            }
+            mService.sendTextMessage(guess.first.getAccountID(), guess.second, txt);
+        } else {
+            mService.sendTextMessage(conference, txt);
+        }
+    }
+
+    @Override
+    public void deleteConversation(Conversation conversation) {
+        if (mService != null) {
+            mService.deleteConversation(conversation);
+            getActivity().finish();
+        }
+    }
+
+    @Override
+    public void copyContactNumberToClipboard(String contactNumber) {
+        ClipboardHelper.copyNumberToClipboard(getActivity(), contactNumber, this);
+    }
+
+    @Override
+    public void clipBoardDidCopyNumber(String copiedNumber) {
+        View view = getActivity().findViewById(android.R.id.content);
+        if (view != null) {
+            String snackbarText = getString(R.string.conversation_action_copied_peer_number_clipboard,
+                    Phone.getShortenedNumber(copiedNumber));
+            Snackbar.make(view, snackbarText, Snackbar.LENGTH_LONG).show();
+        }
+    }
+
+    @Override
+    public void onDetailsLoaded(Bitmap bmp, String formattedName) {
+        ActionBar actionBar = ((ConversationActivity) getActivity()).getSupportActionBar();
+        if (actionBar != null && formattedName != null) {
+            actionBar.setTitle(formattedName);
+        }
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/activity_conversation.xml b/ring-android/app/src/main/res/layout/activity_conversation.xml
index 90ed88f..a2a928a 100644
--- a/ring-android/app/src/main/res/layout/activity_conversation.xml
+++ b/ring-android/app/src/main/res/layout/activity_conversation.xml
@@ -5,7 +5,6 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="#ebeff0"
-    android:orientation="vertical"
     tools:context=".client.ConversationActivity">
 
     <android.support.v7.widget.Toolbar
@@ -25,90 +24,11 @@
         app:elevation="4dp"
         app:titleTextAppearance="@style/ToolbarTitle" />
 
-    <android.support.v7.widget.RecyclerView
-        android:id="@+id/hist_list"
+    <FrameLayout
+        android:id="@+id/main_frame"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_alignParentBottom="true"
-        android:layout_alignParentEnd="true"
+        android:layout_height="match_parent"
         android:layout_alignParentLeft="true"
-        android:layout_alignParentRight="true"
         android:layout_alignParentStart="true"
-        android:layout_below="@id/main_toolbar"
-        android:clipToPadding="false"
-        android:divider="@null"
-        android:listSelector="@android:color/transparent"
-        android:paddingBottom="60dp"
-        android:paddingTop="8dp"
-        tools:listitem="@layout/item_conv_msg_peer" />
-
-    <RelativeLayout
-        android:id="@+id/ongoingcall_pane"
-        android:layout_width="match_parent"
-        android:layout_height="48dp"
-        android:layout_below="@id/main_toolbar"
-        android:background="#e3c1c1">
-
-        <TextView
-            android:id="@+id/textView2"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_centerHorizontal="true"
-            android:layout_centerVertical="true"
-            android:layout_margin="10dp"
-            android:text="@string/ongoing_call"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:textColor="@color/text_color_primary" />
-    </RelativeLayout>
-
-    <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
-        android:id="@+id/userInputMessageCardView"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_alignParentBottom="true"
-        android:layout_alignParentEnd="true"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentRight="true"
-        android:layout_alignParentStart="true"
-        android:layout_marginBottom="16dp"
-        android:layout_marginLeft="8dp"
-        android:layout_marginRight="8dp"
-        card_view:cardCornerRadius="2dp">
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:orientation="horizontal">
-
-            <Spinner
-                android:id="@+id/number_selector"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:visibility="visible"
-                tools:listitem="@layout/item_number_selected" />
-
-            <EditText
-                android:id="@+id/msg_input_txt"
-                android:layout_width="0dp"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:background="@null"
-                android:hint="@string/write_a_message"
-                android:imeOptions="actionSend|flagNoExtractUi"
-                android:inputType="textShortMessage|textImeMultiLine|text|textMultiLine|textCapSentences"
-                android:maxLines="5"
-                android:padding="8dp" />
-
-            <ImageButton
-                android:id="@+id/msg_send"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:background="?selectableItemBackgroundBorderless"
-                android:contentDescription="@string/send_message"
-                android:padding="8dp"
-                android:src="@drawable/ic_send_black"
-                android:tint="@android:color/darker_gray" />
-        </LinearLayout>
-    </android.support.v7.widget.CardView>
-
+        android:layout_below="@+id/main_toolbar" />
 </RelativeLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/frag_conversation.xml b/ring-android/app/src/main/res/layout/frag_conversation.xml
new file mode 100644
index 0000000..90e2b22
--- /dev/null
+++ b/ring-android/app/src/main/res/layout/frag_conversation.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/hist_list"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentTop="true"
+        android:clipToPadding="false"
+        android:divider="@null"
+        android:listSelector="@android:color/transparent"
+        android:paddingBottom="60dp"
+        android:paddingTop="8dp"
+        tools:listitem="@layout/item_conv_msg_peer" />
+
+    <RelativeLayout
+        android:id="@+id/ongoingcall_pane"
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:layout_below="@id/main_toolbar"
+        android:background="#e3c1c1">
+
+        <TextView
+            android:id="@+id/textView2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:layout_centerVertical="true"
+            android:layout_margin="10dp"
+            android:text="@string/ongoing_call"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="@color/text_color_primary" />
+    </RelativeLayout>
+
+    <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/userInputMessageCardView"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentStart="true"
+        android:layout_marginBottom="16dp"
+        android:layout_marginLeft="8dp"
+        android:layout_marginRight="8dp"
+        card_view:cardCornerRadius="2dp">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="horizontal">
+
+            <Spinner
+                android:id="@+id/number_selector"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:visibility="visible"
+                tools:listitem="@layout/item_number_selected" />
+
+            <EditText
+                android:id="@+id/msg_input_txt"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:background="@null"
+                android:hint="@string/write_a_message"
+                android:imeOptions="actionSend|flagNoExtractUi"
+                android:inputType="textShortMessage|textImeMultiLine|text|textMultiLine|textCapSentences"
+                android:maxLines="5"
+                android:padding="8dp" />
+
+            <ImageButton
+                android:id="@+id/msg_send"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:background="?selectableItemBackgroundBorderless"
+                android:contentDescription="@string/send_message"
+                android:padding="8dp"
+                android:src="@drawable/ic_send_black"
+                android:tint="@android:color/darker_gray" />
+        </LinearLayout>
+    </android.support.v7.widget.CardView>
+</RelativeLayout>
\ No newline at end of file