call: add connecting animation

Brings a pulse animation on contact avatar while calling
Clears wrong layout instructions and use ButterKnife for views
injections.
We use a different layout file in landscape to better use the available
screen estate.

Change-Id: Ie40f64bc8951b29cfc2c6b1c7640e05de149ccf9
Tuleap: #834
diff --git a/ring-android/app/build.gradle b/ring-android/app/build.gradle
index 2b94327..9408560 100644
--- a/ring-android/app/build.gradle
+++ b/ring-android/app/build.gradle
@@ -21,6 +21,7 @@
     compile 'com.google.zxing:core:3.2.1'
     compile 'com.journeyapps:zxing-android-embedded:3.2.0@aar'
     compile 'com.jakewharton:butterknife:8.1.0'
+    compile 'com.skyfishjy.ripplebackground:library:1.0.1'
     apt 'com.jakewharton:butterknife-compiler:8.1.0'
 }
 
diff --git a/ring-android/app/src/main/java/cx/ring/client/CallActivity.java b/ring-android/app/src/main/java/cx/ring/client/CallActivity.java
index 71b1518..e3765fc 100644
--- a/ring-android/app/src/main/java/cx/ring/client/CallActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/client/CallActivity.java
@@ -58,7 +58,7 @@
 
 import static cx.ring.service.LocalService.Callbacks;
 
-public class CallActivity extends AppCompatActivity implements Callbacks, CallFragment.Callbacks, CallProximityManager.ProximityDirector {
+public class CallActivity extends AppCompatActivity implements Callbacks, CallFragment.ConversationCallbacks, CallProximityManager.ProximityDirector {
     static final String TAG = CallActivity.class.getSimpleName();
 
     public static final String ACTION_CALL = BuildConfig.APPLICATION_ID + ".action.call";
@@ -96,7 +96,7 @@
 
         setContentView(R.layout.activity_call_layout);
 
-        mMainView = findViewById(R.id.maincalllayout);
+        mMainView = findViewById(R.id.main_call_layout);
         mMainView.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
@@ -135,6 +135,12 @@
             showSystemUI();
         }
 
+        // Reload a new view
+        FragmentManager fragmentManager = getFragmentManager();
+        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
+        mCurrentCallFragment = new CallFragment();
+        fragmentTransaction.replace(R.id.main_call_layout, mCurrentCallFragment).commit();
+
         super.onConfigurationChanged(newConfig);
     }
 
@@ -249,7 +255,7 @@
             FragmentManager fragmentManager = getFragmentManager();
             FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
             mCurrentCallFragment = new CallFragment();
-            fragmentTransaction.add(R.id.maincalllayout, mCurrentCallFragment).commit();
+            fragmentTransaction.add(R.id.main_call_layout, mCurrentCallFragment).commit();
             hideSystemUI();
         }
 
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 b6a0a78..8f31c97 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
@@ -37,6 +37,7 @@
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
 import android.os.RemoteException;
+import android.support.annotation.Nullable;
 import android.support.v4.app.NotificationManagerCompat;
 import android.support.v7.app.ActionBar;
 import android.text.Editable;
@@ -50,7 +51,6 @@
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Button;
@@ -61,9 +61,14 @@
 import android.widget.TextView;
 import android.widget.ViewSwitcher;
 
+import com.skyfishjy.library.RippleBackground;
+
 import java.lang.ref.WeakReference;
 import java.util.HashMap;
 
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
 import cx.ring.R;
 import cx.ring.adapters.ContactPictureTask;
 import cx.ring.client.ConversationActivity;
@@ -81,7 +86,7 @@
 
 public class CallFragment extends Fragment implements CallInterface {
 
-    static private final String TAG = CallFragment.class.getSimpleName();
+    static final private String TAG = CallFragment.class.getSimpleName();
 
     public static final int REQUEST_TRANSFER = 10;
 
@@ -90,38 +95,65 @@
 
     // Screen wake lock for incoming call
     private WakeLock mScreenWakeLock;
-    private View contactBubbleLayout;
-    private ImageView contactBubbleView;
-    private TextView contactBubbleTxt;
-    private TextView contactBubbleNumTxt;
-    private View acceptButton;
-    private View refuseButton;
-    private View hangupButton;
-    private View securityIndicator;
+
+    @BindView(R.id.contact_bubble_layout)
+    View contactBubbleLayout;
+
+    @BindView(R.id.contact_bubble)
+    ImageView contactBubbleView;
+
+    @BindView(R.id.contact_bubble_txt)
+    TextView contactBubbleTxt;
+
+    @BindView(R.id.contact_bubble_num_txt)
+    TextView contactBubbleNumTxt;
+
+    @BindView(R.id.call_accept_btn)
+    View acceptButton;
+
+    @BindView(R.id.call_refuse_btn)
+    View refuseButton;
+
+    @BindView(R.id.call_hangup_btn)
+    View hangupButton;
+
+    @BindView(R.id.call_status_txt)
+    TextView mCallStatusTxt;
+
+    @BindView(R.id.security_indicator)
+    View securityIndicator;
+
+    @BindView(R.id.security_switcher)
+    ViewSwitcher mSecuritySwitch;
+
+    @BindView(R.id.dialpad_edit_text)
+    EditText mNumeralDialEditText;
+
+    @BindView(R.id.ripple_animation)
+    RippleBackground mPulseAnimation;
+
+    @BindView(R.id.video_preview_surface)
+    SurfaceView mVideoSurface = null;
+
     private MenuItem speakerPhoneBtn = null;
     private MenuItem addContactBtn = null;
     private MenuItem flipCameraBtn = null;
     private MenuItem dialPadBtn = null;
-    private SurfaceView video = null;
-    private SurfaceView videoPreview = null;
 
-    ViewSwitcher mSecuritySwitch;
-    private TextView mCallStatusTxt;
+    @BindView(R.id.camera_preview_surface)
+    SurfaceView videoPreview = null;
 
-    public Callbacks mCallbacks = sDummyCallbacks;
+    public ConversationCallbacks mCallbacks = sDummyCallbacks;
 
     private AudioManager audioManager;
-
     private boolean haveVideo = false;
     private int videoWidth = -1, videoHeight = -1;
     private int previewWidth = -1, previewHeight = -1;
+
     private boolean lastVideoSource = true;
-
     private Conference mCachedConference = null;
-    private ViewGroup rootView = null;
-    private boolean ongoingCall = false;
 
-    private EditText mNumeralDialEditText;
+    private boolean ongoingCall = false;
 
     private DisplayManager.DisplayListener displayListener;
 
@@ -130,11 +162,11 @@
         Log.i(TAG, "onAttach");
         super.onAttach(activity);
 
-        if (!(activity instanceof Callbacks)) {
+        if (!(activity instanceof ConversationCallbacks)) {
             throw new IllegalStateException("Activity must implement fragment's callbacks.");
         }
 
-        mCallbacks = (Callbacks) activity;
+        mCallbacks = (ConversationCallbacks) activity;
 
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(CallManagerCallBack.RECORD_STATE_CHANGED);
@@ -201,23 +233,21 @@
                 }
             };
         }
-
-        setRetainInstance(true);
     }
 
     @Override
     public void onDestroy() {
+        super.onDestroy();
         Log.i(TAG, "onDestroy");
         if (mScreenWakeLock != null && mScreenWakeLock.isHeld()) {
             mScreenWakeLock.release();
         }
-        super.onDestroy();
     }
 
     /**
      * The Activity calling this fragment has to implement this interface
      */
-    public interface Callbacks extends LocalService.Callbacks {
+    public interface ConversationCallbacks extends LocalService.Callbacks {
         void startTimer();
 
         void terminateCall();
@@ -229,34 +259,46 @@
         ActionBar getSupportActionBar();
     }
 
-    /**
-     * A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity.
-     */
-    private static class DummyCallbacks extends LocalService.DummyCallbacks implements Callbacks {
+    private static final ConversationCallbacks sDummyCallbacks = new ConversationCallbacks() {
+        @Override
+        public void startTimer() {
+            //Dummy implementation
+        }
+
         @Override
         public void terminateCall() {
+            //Dummy implementation
         }
 
         @Override
         public Conference getDisplayedConference() {
+            //Dummy implementation
             return null;
         }
 
         @Override
         public void updateDisplayedConference(Conference c) {
+            //Dummy implementation
         }
 
         @Override
         public ActionBar getSupportActionBar() {
+            //Dummy implementation
             return null;
         }
 
         @Override
-        public void startTimer() {
+        public IDRingService getRemoteService() {
+            //Dummy implementation
+            return null;
         }
-    }
 
-    private static final Callbacks sDummyCallbacks = new DummyCallbacks();
+        @Override
+        public LocalService getService() {
+            //Dummy implementation
+            return null;
+        }
+    };
 
     public class CallReceiver extends BroadcastReceiver {
         private final String TAG = CallReceiver.class.getSimpleName();
@@ -267,25 +309,25 @@
             if (action.contentEquals(LocalService.ACTION_CONF_UPDATE)) {
                 confUpdate();
             } else if (action.contentEquals(DRingService.VIDEO_EVENT)) {
-                if (video == null)
+                if (mVideoSurface == null)
                     return;
                 Conference conf = getConference();
                 if (intent.hasExtra("start")) {
-                    video.setVisibility(View.VISIBLE);
+                    mVideoSurface.setVisibility(View.VISIBLE);
                     videoPreview.setVisibility(View.VISIBLE);
                 } else if (intent.hasExtra("camera")) {
                     previewWidth = intent.getIntExtra("width", 0);
                     previewHeight = intent.getIntExtra("height", 0);
                 } else if (conf != null && conf.getId().equals(intent.getStringExtra("call"))) {
-                    if (video != null) {
+                    if (mVideoSurface != null) {
                         haveVideo = intent.getBooleanExtra("started", false);
                         if (haveVideo) {
-                            video.setVisibility(View.VISIBLE);
+                            mVideoSurface.setVisibility(View.VISIBLE);
                             videoPreview.setVisibility(View.VISIBLE);
                             videoWidth = intent.getIntExtra("width", 0);
                             videoHeight = intent.getIntExtra("height", 0);
                         } else {
-                            video.setVisibility(View.GONE);
+                            mVideoSurface.setVisibility(View.GONE);
                             videoPreview.setVisibility(View.GONE);
                         }
                     }
@@ -362,7 +404,7 @@
             speakerPhoneBtn.setChecked(speakerPhone);
         }
         if (addContactBtn != null) {
-            SipCall call = (getConference() != null && !getConference().getParticipants().isEmpty()) ? getConference().getParticipants().get(0) : null;
+            SipCall call = (getConference() != null && !getConference().getParticipants().isEmpty()) ? getFirstParticipant() : null;
             addContactBtn.setVisible(call != null && null != call.getContact() && call.getContact().isUnknown());
         }
 
@@ -384,12 +426,12 @@
                 Intent intent = new Intent()
                         .setClass(getActivity(), ConversationActivity.class)
                         .setAction(Intent.ACTION_VIEW)
-                        .setData(Uri.withAppendedPath(ConversationActivity.CONTENT_URI, getConference().getParticipants().get(0).getContact().getIds().get(0)));
+                        .setData(Uri.withAppendedPath(ConversationActivity.CONTENT_URI, getFirstParticipant().getContact().getIds().get(0)));
                 intent.putExtra("resuming", true);
                 startActivityForResult(intent, HomeActivity.REQUEST_CODE_CONVERSATION);
                 break;
             case R.id.menuitem_addcontact:
-                startActivityForResult(getConference().getParticipants().get(0).getContact().getAddNumberIntent(), ConversationActivity.REQ_ADD_CONTACT);
+                startActivityForResult(getFirstParticipant().getContact().getAddNumberIntent(), ConversationActivity.REQ_ADD_CONTACT);
                 break;
             case R.id.menuitem_speaker:
                 audioManager.setSpeakerphoneOn(!audioManager.isSpeakerphoneOn());
@@ -448,10 +490,10 @@
         }
 
         Conference c = getConference();
-        if (c != null && video != null && c.resumeVideo) {
-            Log.w(TAG, "Resuming video");
+        if (c != null && mVideoSurface != null && c.resumeVideo) {
+            Log.i(TAG, "Resuming video");
             haveVideo = true;
-            video.setVisibility(View.VISIBLE);
+            mVideoSurface.setVisibility(View.VISIBLE);
             videoPreview.setVisibility(View.VISIBLE);
             c.resumeVideo = false;
         }
@@ -459,8 +501,8 @@
 
     @Override
     public void onResume() {
-        Log.w(TAG, "onResume()");
         super.onResume();
+        Log.i(TAG, "onResume()");
         Conference c = getConference();
 
         this.confUpdate();
@@ -476,7 +518,7 @@
             if (c.resumeVideo) {
                 Log.w(TAG, "Resuming video");
                 haveVideo = true;
-                video.setVisibility(View.VISIBLE);
+                mVideoSurface.setVisibility(View.VISIBLE);
                 videoPreview.setVisibility(View.VISIBLE);
                 c.resumeVideo = false;
             }
@@ -576,7 +618,7 @@
 
     @Override
     public void rtcpReportReceived(Conference c, HashMap<String, Integer> stats) {
-
+        // No implementation yet
     }
 
     @Override
@@ -589,9 +631,7 @@
                     Conference c = data.getParcelableExtra("target");
                     transfer = data.getParcelableExtra("transfer");
                     try {
-
                         mCallbacks.getRemoteService().attendedTransfer(transfer.getCallId(), c.getParticipants().get(0).getCallId());
-
                     } catch (RemoteException e) {
                         e.printStackTrace();
                     }
@@ -616,13 +656,17 @@
     }
 
     void resetVideoSizes() {
-        double video_ratio = videoWidth / (double) videoHeight;
-        double screen_ratio = rootView.getWidth() / (double) rootView.getHeight();
+        ViewGroup rootView = (ViewGroup) getView();
+        if (rootView == null)
+            return;
 
-        FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) video.getLayoutParams();
+        double videoRatio = videoWidth / (double) videoHeight;
+        double screenRatio = getView().getWidth() / (double) getView().getHeight();
+
+        FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mVideoSurface.getLayoutParams();
         int oldW = params.width;
         int oldH = params.height;
-        if (video_ratio >= screen_ratio) {
+        if (videoRatio >= screenRatio) {
             params.width = RelativeLayout.LayoutParams.MATCH_PARENT;
             params.height = (int) (videoHeight * (double) rootView.getWidth() / (double) videoWidth);
         } else {
@@ -632,20 +676,20 @@
 
         if (oldW != params.width || oldH != params.height) {
             Log.w(TAG, "onLayoutChange " + params.width + " x " + params.height);
-            video.setLayoutParams(params);
+            mVideoSurface.setLayoutParams(params);
         }
 
         DisplayMetrics metrics = getResources().getDisplayMetrics();
-        FrameLayout.LayoutParams params_preview = (FrameLayout.LayoutParams) videoPreview.getLayoutParams();
-        oldW = params_preview.width;
-        oldH = params_preview.height;
-        double preview_max_dim = Math.max(previewWidth, previewHeight);
-        double preview_ratio = metrics.density * 160. / preview_max_dim;
-        params_preview.width = (int) (previewWidth * preview_ratio);
-        params_preview.height = (int) (previewHeight * preview_ratio);
-        if (oldW != params_preview.width || oldH != params_preview.height) {
-            Log.w(TAG, "onLayoutChange " + params_preview.width + " x " + params_preview.height);
-            videoPreview.setLayoutParams(params_preview);
+        FrameLayout.LayoutParams paramsPreview = (FrameLayout.LayoutParams) videoPreview.getLayoutParams();
+        oldW = paramsPreview.width;
+        oldH = paramsPreview.height;
+        double previewMaxDim = Math.max(previewWidth, previewHeight);
+        double previewRatio = metrics.density * 160. / previewMaxDim;
+        paramsPreview.width = (int) (previewWidth * previewRatio);
+        paramsPreview.height = (int) (previewHeight * previewRatio);
+        if (oldW != paramsPreview.width || oldH != paramsPreview.height) {
+            Log.i(TAG, "onLayoutChange " + paramsPreview.width + " x " + paramsPreview.height);
+            videoPreview.setLayoutParams(paramsPreview);
         }
     }
 
@@ -664,9 +708,10 @@
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         Log.i(TAG, "onCreateView");
-        rootView = (ViewGroup) inflater.inflate(R.layout.frag_call, container, false);
+        View rootView = inflater.inflate(R.layout.frag_call, container, false);
 
-        mNumeralDialEditText = (EditText) rootView.findViewById(R.id.dialpad_edit_text);
+        ButterKnife.bind(this, rootView);
+
         mNumeralDialEditText.requestFocus();
         mNumeralDialEditText.addTextChangedListener(new TextWatcher() {
             @Override
@@ -694,20 +739,9 @@
             }
         });
 
-        contactBubbleLayout = rootView.findViewById(R.id.contact_bubble_layout);
-        contactBubbleView = (ImageView) rootView.findViewById(R.id.contact_bubble);
-        contactBubbleTxt = (TextView) rootView.findViewById(R.id.contact_bubble_txt);
-        contactBubbleNumTxt = (TextView) rootView.findViewById(R.id.contact_bubble_num_txt);
-        acceptButton = rootView.findViewById(R.id.call_accept_btn);
-        refuseButton = rootView.findViewById(R.id.call_refuse_btn);
-        hangupButton = rootView.findViewById(R.id.call_hangup_btn);
-        mCallStatusTxt = (TextView) rootView.findViewById(R.id.call_status_txt);
-        mSecuritySwitch = (ViewSwitcher) rootView.findViewById(R.id.security_switcher);
-        securityIndicator = rootView.findViewById(R.id.security_indicator);
 
-        video = (SurfaceView) rootView.findViewById(R.id.video_preview_surface);
-        video.getHolder().setFormat(PixelFormat.RGBA_8888);
-        video.getHolder().addCallback(new SurfaceHolder.Callback() {
+        mVideoSurface.getHolder().setFormat(PixelFormat.RGBA_8888);
+        mVideoSurface.getHolder().addCallback(new SurfaceHolder.Callback() {
             @Override
             public void surfaceCreated(SurfaceHolder holder) {
                 contactBubbleLayout.setVisibility(View.GONE);
@@ -755,7 +789,6 @@
             }
         });
 
-        videoPreview = (SurfaceView) rootView.findViewById(R.id.camera_preview_surface);
         videoPreview.getHolder().setFormat(PixelFormat.RGBA_8888);
         videoPreview.getHolder().addCallback(new SurfaceHolder.Callback() {
             @Override
@@ -803,6 +836,7 @@
     }
 
     private void initContactDisplay(final SipCall call) {
+
         CallContact contact = call.getContact();
         final String name = contact.getDisplayName();
         contactBubbleTxt.setText(name);
@@ -812,7 +846,11 @@
             contactBubbleNumTxt.setVisibility(View.VISIBLE);
             contactBubbleNumTxt.setText(call.getNumber());
         }
+
+        mPulseAnimation.startRippleAnimation();
+
         new ContactPictureTask(getActivity(), contactBubbleView, contact).run();
+
         ActionBar ab = mCallbacks.getSupportActionBar();
         ab.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE);
         ab.setTitle(name);
@@ -823,30 +861,16 @@
         mCallbacks.startTimer();
         acceptButton.setVisibility(View.GONE);
         refuseButton.setVisibility(View.GONE);
+        hangupButton.setVisibility(View.VISIBLE);
 
-        final SipCall call = getConference().getParticipants().get(0);
-        final String call_id = call.getCallId();
+        final SipCall call = getFirstParticipant();
         initContactDisplay(call);
 
         if (getActivity() != null) {
             getActivity().invalidateOptionsMenu();
         }
 
-        hangupButton.setVisibility(View.VISIBLE);
-        hangupButton.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                try {
-                    mCallbacks.getRemoteService().hangUp(call_id);
-                    mCallbacks.terminateCall();
-                } catch (RemoteException e) {
-                    e.printStackTrace();
-                }
-            }
-        });
-
         contactBubbleLayout.setVisibility(haveVideo ? View.GONE : View.VISIBLE);
-
         updateSecurityDisplay();
     }
 
@@ -864,7 +888,7 @@
         if (getConference().hasMultipleParticipants()) {
             //TODO What layout should we put?
         } else {
-            final SecureSipCall secured = (SecureSipCall) getConference().getParticipants().get(0);
+            final SecureSipCall secured = (SecureSipCall) getFirstParticipant();
             switch (secured.displayModule()) {
                 case SecureSipCall.DISPLAY_GREEN_LOCK:
                     Log.i(TAG, "DISPLAY_GREEN_LOCK");
@@ -903,17 +927,9 @@
         mSecuritySwitch.setVisibility(View.VISIBLE);
     }
 
-    /*protected Bitmap getContactPhoto(CallContact contact, int size) {
-        if (contact.getPhotoId() > 0) {
-            return ContactPictureTask.loadContactPhoto(getActivity().getContentResolver(), contact.getId());
-        } else {
-            return ContactPictureTask.decodeSampledBitmapFromResource(getResources(), R.drawable.ic_contact_picture, size, size);
-        }
-    }*/
-
     private void initIncomingCallDisplay() {
         Log.i(TAG, "Start incoming display");
-        final SipCall call = getConference().getParticipants().get(0);
+        final SipCall call = getFirstParticipant();
         if (mCallbacks.getService().getAccount(call.getAccount()).isAutoanswerEnabled()) {
             try {
                 mCallbacks.getRemoteService().accept(call.getCallId());
@@ -923,32 +939,7 @@
         } else {
             initContactDisplay(call);
             acceptButton.setVisibility(View.VISIBLE);
-            acceptButton.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    acceptButton.setOnClickListener(null);
-                    refuseButton.setOnClickListener(null);
-                    try {
-                        mCallbacks.getRemoteService().accept(call.getCallId());
-                    } catch (RemoteException e) {
-                        e.printStackTrace();
-                    }
-                }
-            });
             refuseButton.setVisibility(View.VISIBLE);
-            refuseButton.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    acceptButton.setOnClickListener(null);
-                    refuseButton.setOnClickListener(null);
-                    try {
-                        mCallbacks.getRemoteService().refuse(call.getCallId());
-                        mCallbacks.terminateCall();
-                    } catch (RemoteException e) {
-                        e.printStackTrace();
-                    }
-                }
-            });
             hangupButton.setVisibility(View.GONE);
         }
     }
@@ -956,51 +947,60 @@
     private void initOutGoingCallDisplay() {
         Log.i(TAG, "Start outgoing display");
 
-        final SipCall call = getConference().getParticipants().get(0);
+        final SipCall call = getFirstParticipant();
         initContactDisplay(call);
 
         acceptButton.setVisibility(View.GONE);
-        refuseButton.setVisibility(View.GONE);
-
-        hangupButton.setVisibility(View.VISIBLE);
-        hangupButton.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                try {
-                    mCallbacks.getRemoteService().hangUp(call.getCallId());
-                    mCallbacks.terminateCall();
-                } catch (RemoteException e) {
-                    e.printStackTrace();
-                }
-            }
-        });
-
+        refuseButton.setVisibility(View.VISIBLE);
+        hangupButton.setVisibility(View.GONE);
     }
 
-    /*
-    public void makeTransfer(BubbleContact contact) {
-        FragmentManager fm = getFragmentManager();
-        editName = TransferDFragment.newInstance();
-        Bundle b = new Bundle();
-        try {
-            b.putParcelableArrayList("calls", (ArrayList<Conference>) mCallbacks.getRemoteService().getConcurrentCalls());
-            b.putParcelable("call_selected", contact.associated_call);
-            editName.setArguments(b);
-            editName.setTargetFragment(this, REQUEST_TRANSFER);
-            editName.show(fm, "");
-        } catch (RemoteException e) {
-            Log.e(TAG, e.toString());
-        }
-
-    }*/
-
     public void updateTime() {
         if (getConference() != null && !getConference().getParticipants().isEmpty()) {
-            long duration = System.currentTimeMillis() - getConference().getParticipants().get(0).getTimestampStart();
+            long duration = System.currentTimeMillis() - getFirstParticipant().getTimestampStart();
             duration = duration / 1000;
             if (getConference().isOnGoing())
                 mCallStatusTxt.setText(String.format("%d:%02d:%02d", duration / 3600, duration % 3600 / 60, duration % 60));
         }
+    }
 
+    @OnClick({R.id.call_hangup_btn, R.id.call_refuse_btn})
+    public void hangUpClicked() {
+        try {
+            final SipCall call = getFirstParticipant();
+            if (call == null) {
+                return;
+            }
+            final String callId = call.getCallId();
+            mCallbacks.getRemoteService().hangUp(callId);
+            mCallbacks.terminateCall();
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @OnClick(R.id.call_accept_btn)
+    public void acceptClicked() {
+        final SipCall call = getFirstParticipant();
+        if (call == null) {
+            return;
+        }
+        try {
+            mCallbacks.getRemoteService().accept(call.getCallId());
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Helper accessor that check nullity or emptiness of components to access first call participant
+     * @return the first participant or null
+     */
+    @Nullable
+    private SipCall getFirstParticipant() {
+        if (getConference() == null || getConference().getParticipants() == null || getConference().getParticipants().isEmpty()) {
+            return null;
+        }
+        return getConference().getParticipants().get(0);
     }
 }
diff --git a/ring-android/app/src/main/res/layout-land/frag_call.xml b/ring-android/app/src/main/res/layout-land/frag_call.xml
new file mode 100644
index 0000000..8fa4aa6
--- /dev/null
+++ b/ring-android/app/src/main/res/layout-land/frag_call.xml
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+Copyright (C) 2004-2016 Savoir-faire Linux Inc.
+
+Author: Adrien Beraud <adrien.beraud@savoirfairelinux.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".client.CallActivity">
+
+    <SurfaceView
+        android:id="@+id/video_preview_surface"
+        android:layout_width="match_parent"
+        android:layout_height="32dp"
+        android:layout_gravity="center"
+        android:visibility="gone" />
+
+    <FrameLayout
+        android:id="@+id/inner_layout"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:fitsSystemWindows="true">
+
+        <SurfaceView
+            android:id="@+id/camera_preview_surface"
+            android:layout_width="160dp"
+            android:layout_height="120dp"
+            android:layout_gravity="bottom|end"
+            android:layout_margin="8dp"
+            android:visibility="gone" />
+
+        <LinearLayout
+            android:id="@+id/contact_bubble_layout"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:weightSum="100"
+            android:orientation="horizontal"
+            android:layout_marginBottom="16dp">
+
+            <com.skyfishjy.library.RippleBackground
+                android:id="@+id/ripple_animation"
+                android:layout_width="0dp"
+                android:layout_weight="50"
+                android:layout_height="match_parent"
+                app:rb_color="@color/white"
+                app:rb_duration="5000"
+                app:rb_radius="20dp"
+                app:rb_rippleAmount="3"
+                app:rb_scale="6">
+
+                <ImageView
+                    android:id="@+id/contact_bubble"
+                    android:layout_width="160dp"
+                    android:layout_height="160dp"
+                    android:layout_centerInParent="true"
+                    tools:src="@drawable/ic_contact_picture" />
+            </com.skyfishjy.library.RippleBackground>
+
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:gravity="center"
+                android:layout_weight="50"
+                android:layout_margin="10dp"
+                android:orientation="vertical">
+
+                <TextView
+                    android:id="@+id/contact_bubble_txt"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_horizontal"
+                    android:singleLine="true"
+                    android:textAppearance="?android:attr/textAppearanceLarge"
+                    android:textColor="@color/text_color_primary_dark"
+                    tools:text="Contact Name" />
+
+                <TextView
+                    android:id="@+id/contact_bubble_num_txt"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_horizontal"
+                    android:ellipsize="end"
+                    android:paddingEnd="32dp"
+                    android:paddingStart="32dp"
+                    android:singleLine="true"
+                    android:textAppearance="?android:attr/textAppearanceMedium"
+                    android:textColor="@color/text_color_secondary_dark"
+                    tools:text="ring:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
+
+                <TextView
+                    android:id="@+id/call_status_txt"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_horizontal"
+                    android:textColor="@color/text_color_primary_dark"
+                    android:textSize="16sp"
+                    tools:text="Connecting" />
+
+                <LinearLayout
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_margin="12dp"
+                    android:orientation="horizontal">
+
+                    <android.support.design.widget.FloatingActionButton
+                        android:id="@+id/call_refuse_btn"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_margin="16dp"
+                        android:contentDescription="@string/action_call_decline"
+                        android:src="@drawable/ic_call_end_white_24dp"
+                        app:backgroundTint="@color/error_red"
+                        app:elevation="6dp"
+                        app:pressedTranslationZ="12dp"
+                        app:rippleColor="@android:color/white" />
+
+                    <android.support.design.widget.FloatingActionButton
+                        android:id="@+id/call_accept_btn"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_margin="16dp"
+                        android:contentDescription="@string/action_call_accept"
+                        android:src="@drawable/ic_call_white_24dp"
+                        app:backgroundTint="#4caf50"
+                        app:elevation="6dp"
+                        app:pressedTranslationZ="12dp"
+                        app:rippleColor="@android:color/white" />
+                </LinearLayout>
+
+                <ImageView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:id="@+id/imageView"
+                    android:layout_gravity="center_horizontal" />
+
+
+            </LinearLayout>
+        </LinearLayout>
+
+        <RelativeLayout
+            android:id="@+id/call_status_bar"
+            android:layout_width="match_parent"
+            android:layout_height="?android:attr/actionBarSize"
+            android:visibility="visible">
+
+            <ViewSwitcher
+                android:id="@+id/security_switcher"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerVertical="true"
+                android:visibility="gone">
+
+                <Button
+                    android:id="@+id/confirm_sas"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center"
+                    android:textColor="@android:color/white"
+                    android:textSize="12sp" />
+
+                <ImageView
+                    android:id="@+id/lock_image"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="end|center_vertical" />
+            </ViewSwitcher>
+
+            <ImageView
+                android:id="@+id/security_indicator"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:layout_alignParentRight="true"
+                android:layout_centerVertical="true"
+                android:layout_margin="16dp"
+                android:src="@drawable/ic_lock_white_24dp"
+                android:tint="#4caf50"
+                android:visibility="gone" />
+
+        </RelativeLayout>
+
+        <android.support.design.widget.FloatingActionButton
+            android:id="@+id/call_hangup_btn"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|center_horizontal"
+            android:layout_margin="28dp"
+            android:src="@drawable/ic_call_end_white_24dp"
+            app:backgroundTint="@color/error_red"
+            app:elevation="6dp"
+            app:pressedTranslationZ="12dp"
+            app:rippleColor="@android:color/white" />
+
+        <EditText
+            android:id="@+id/dialpad_edit_text"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:ems="10"
+            android:inputType="phone"
+            android:visibility="visible" />
+
+    </FrameLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/activity_call_layout.xml b/ring-android/app/src/main/res/layout/activity_call_layout.xml
index f12e443..cf29ef5 100644
--- a/ring-android/app/src/main/res/layout/activity_call_layout.xml
+++ b/ring-android/app/src/main/res/layout/activity_call_layout.xml
@@ -20,7 +20,7 @@
 
 <cx.ring.views.FitsWindowsLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/maincalllayout"
+    android:id="@+id/main_call_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@color/cardview_dark_background"
diff --git a/ring-android/app/src/main/res/layout/frag_call.xml b/ring-android/app/src/main/res/layout/frag_call.xml
index 656d3c7..7c9f8c9 100644
--- a/ring-android/app/src/main/res/layout/frag_call.xml
+++ b/ring-android/app/src/main/res/layout/frag_call.xml
@@ -44,53 +44,76 @@
             android:layout_margin="8dp"
             android:visibility="gone" />
 
-        <LinearLayout
+        <RelativeLayout
             android:id="@+id/contact_bubble_layout"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical"
-            android:layout_marginBottom="16dp"
-            android:gravity="center_horizontal"
-            android:orientation="vertical">
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_gravity="center"
+            android:layout_marginBottom="16dp">
 
-            <ImageView
-                android:id="@+id/contact_bubble"
-                android:layout_width="160dp"
-                android:layout_height="160dp"
-                android:layout_marginBottom="16dp"
-                android:layout_marginLeft="16dp"
-                android:layout_marginRight="16dp" />
+            <com.skyfishjy.library.RippleBackground
+                android:id="@+id/ripple_animation"
+                android:layout_width="230dp"
+                android:layout_height="230dp"
+                android:layout_centerInParent="true"
+                app:rb_color="@color/white"
+                app:rb_duration="5000"
+                app:rb_radius="20dp"
+                app:rb_rippleAmount="3"
+                app:rb_scale="6">
 
-            <TextView
-                android:id="@+id/contact_bubble_txt"
-                android:layout_width="match_parent"
+                <ImageView
+                    android:id="@+id/contact_bubble"
+                    android:layout_width="160dp"
+                    android:layout_height="160dp"
+                    android:layout_centerInParent="true"
+                    tools:src="@drawable/ic_contact_picture" />
+            </com.skyfishjy.library.RippleBackground>
+
+            <LinearLayout
+                android:layout_width="300dp"
                 android:layout_height="wrap_content"
-                android:gravity="center_horizontal"
-                android:singleLine="true"
-                android:textAppearance="?android:attr/textAppearanceLarge"
-                android:textColor="@color/text_color_primary_dark" />
+                android:layout_below="@id/ripple_animation"
+                android:layout_centerHorizontal="true"
+                android:layout_margin="10dp"
+                android:orientation="vertical">
 
-            <TextView
-                android:id="@+id/contact_bubble_num_txt"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:ellipsize="middle"
-                android:gravity="center_horizontal"
-                android:paddingEnd="8dp"
-                android:paddingStart="8dp"
-                android:singleLine="true"
-                android:textAppearance="?android:attr/textAppearanceMedium"
-                android:textColor="@color/text_color_secondary_dark" />
+                <TextView
+                    android:id="@+id/contact_bubble_txt"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_horizontal"
+                    android:singleLine="true"
+                    android:textAppearance="?android:attr/textAppearanceLarge"
+                    android:textColor="@color/text_color_primary_dark"
+                    tools:text="Contact Name" />
 
-            <TextView
-                android:id="@+id/call_status_txt"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:textAppearance="?android:attr/textAppearanceMedium"
-                android:textColor="@color/text_color_primary_dark"
-                android:textSize="16sp" />
+                <TextView
+                    android:id="@+id/contact_bubble_num_txt"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_horizontal"
+                    android:ellipsize="end"
+                    android:paddingEnd="32dp"
+                    android:paddingStart="32dp"
+                    android:singleLine="true"
+                    android:textAppearance="?android:attr/textAppearanceMedium"
+                    android:textColor="@color/text_color_secondary_dark"
+                    tools:text="ring:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
 
-        </LinearLayout>
+                <TextView
+                    android:id="@+id/call_status_txt"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_horizontal"
+                    android:textColor="@color/text_color_primary_dark"
+                    android:textSize="16sp"
+                    tools:text="Connecting" />
+
+            </LinearLayout>
+
+
+        </RelativeLayout>
 
         <LinearLayout
             android:layout_width="wrap_content"