Call: handle media buttons

Also works through bluetooth

Change-Id: I419f1832ba9e92b1230381d2ca73ed51d49de077
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 f6c743f..65bab4d 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
@@ -21,12 +21,14 @@
  */
 package cx.ring.client;
 
+import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.support.v7.app.AppCompatActivity;
+import android.view.KeyEvent;
 import android.view.View;
 
 import cx.ring.BuildConfig;
@@ -34,16 +36,18 @@
 import cx.ring.fragments.CallFragment;
 import cx.ring.fragments.ConversationFragment;
 import cx.ring.services.NotificationService;
+import cx.ring.utils.MediaButtonsHelper;
 
 public class CallActivity extends AppCompatActivity {
     public static final String ACTION_CALL = BuildConfig.APPLICATION_ID + ".action.call";
 
+    private static final String TAG = CallActivity.class.getSimpleName();
+    private static final String CALL_FRAGMENT_TAG = "CALL_FRAGMENT_TAG";
+
     /* result code sent in case of call failure */
     public static int RESULT_FAILURE = -10;
     private View mMainView;
     private int currentOrientation = Configuration.ORIENTATION_PORTRAIT;
-
-
     private boolean dimmed = false;
 
     @Override
@@ -66,6 +70,7 @@
         });
 
         String action = getIntent().getAction();
+        CallFragment callFragment;
         if (Intent.ACTION_CALL.equals(action) || ACTION_CALL.equals(action)) {
 
             boolean audioOnly = getIntent().getBooleanExtra(CallFragment.KEY_AUDIO_ONLY, true);
@@ -75,7 +80,7 @@
             // Reload a new view
             FragmentManager fragmentManager = getFragmentManager();
             FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
-            CallFragment callFragment = CallFragment.newInstance(CallFragment.ACTION_PLACE_CALL,
+            callFragment = CallFragment.newInstance(CallFragment.ACTION_PLACE_CALL,
                     accountId,
                     contactRingId,
                     audioOnly);
@@ -86,11 +91,10 @@
             // Reload a new view
             FragmentManager fragmentManager = getFragmentManager();
             FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
-            CallFragment callFragment = CallFragment.newInstance(CallFragment.ACTION_GET_CALL,
+            callFragment = CallFragment.newInstance(CallFragment.ACTION_GET_CALL,
                     confId);
             fragmentTransaction.replace(R.id.main_call_layout, callFragment).commit();
         }
-
     }
 
     @Override
@@ -127,7 +131,7 @@
     }
 
     // This snippet shows the system bars. It does this by removing all the flags
-// except for the ones that make the content appear under the system bars.
+    // except for the ones that make the content appear under the system bars.
     private void showSystemUI() {
         if (mMainView != null) {
             mMainView.setSystemUiVisibility(
@@ -147,4 +151,17 @@
             showSystemUI();
         }
     }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        Fragment fragment = getFragmentManager().findFragmentByTag(CALL_FRAGMENT_TAG);
+        if (fragment instanceof CallFragment) {
+            CallFragment callFragment = (CallFragment) fragment;
+
+            return MediaButtonsHelper.handleMediaKeyCode(keyCode, callFragment)
+                    || super.onKeyDown(keyCode, event);
+        }
+
+        return super.onKeyDown(keyCode, event);
+    }
 }
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 c6bbcb5..9523147 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
@@ -24,11 +24,13 @@
 import android.os.Bundle;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.Toolbar;
+import android.view.KeyEvent;
 
 import butterknife.BindView;
 import butterknife.ButterKnife;
 import cx.ring.R;
 import cx.ring.fragments.ConversationFragment;
+import cx.ring.utils.MediaButtonsHelper;
 
 public class ConversationActivity extends AppCompatActivity {
 
@@ -74,4 +76,10 @@
         startActivity(new Intent(this, HomeActivity.class));
         finish();
     }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        return MediaButtonsHelper.handleMediaKeyCode(keyCode, mConversationFragment)
+                || super.onKeyDown(keyCode, event);
+    }
 }
diff --git a/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java b/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java
index 7afd177..db1b83e 100644
--- a/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java
@@ -291,7 +291,7 @@
 
         mIsMigrationDialogAlreadyShowed = true;
 
-        AlertDialog.Builder builder = new AlertDialog.Builder(HomeActivity.this)
+        new AlertDialog.Builder(HomeActivity.this)
                 .setTitle(R.string.account_migration_title_dialog)
                 .setMessage(R.string.account_migration_message_dialog)
                 .setIcon(R.drawable.ic_warning)
@@ -314,14 +314,14 @@
                     public void onCancel(DialogInterface dialog) {
                         dialog.dismiss();
                     }
-                });
-        builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
-            @Override
-            public void onDismiss(DialogInterface dialog) {
-                dialog.dismiss();
-            }
-        });
-        builder.show();
+                })
+                .setOnDismissListener(new DialogInterface.OnDismissListener() {
+                    @Override
+                    public void onDismiss(DialogInterface dialog) {
+                        dialog.dismiss();
+                    }
+                })
+                .show();
     }
 
     @Override
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 df0296f..6fc1e3f 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
@@ -65,8 +65,9 @@
 import cx.ring.utils.ActionHelper;
 import cx.ring.utils.CircleTransform;
 import cx.ring.utils.KeyboardVisibilityManager;
+import cx.ring.utils.MediaButtonsHelper;
 
-public class CallFragment extends BaseFragment<CallPresenter> implements CallView {
+public class CallFragment extends BaseFragment<CallPresenter> implements CallView, MediaButtonsHelper.MediaButtonsHelperCallback {
 
     public static final String TAG = CallFragment.class.getSimpleName();
 
@@ -634,4 +635,19 @@
     public void acceptClicked() {
         presenter.acceptCall();
     }
+
+    @Override
+    public void positiveButtonClicked() {
+        presenter.positiveButtonClicked();
+    }
+
+    @Override
+    public void negativeButtonClicked() {
+        presenter.negativeButtonClicked();
+    }
+
+    @Override
+    public void toggleButtonClicked() {
+        presenter.toggleButtonClicked();
+    }
 }
\ No newline at end of file
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
index d51b719..5210c7c 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java
@@ -62,6 +62,7 @@
 import cx.ring.utils.ActionHelper;
 import cx.ring.utils.BitmapUtils;
 import cx.ring.utils.ClipboardHelper;
+import cx.ring.utils.MediaButtonsHelper;
 import cx.ring.utils.VCardUtils;
 import ezvcard.VCard;
 import ezvcard.parameter.ImageType;
@@ -71,6 +72,7 @@
 public class ConversationFragment extends BaseFragment<ConversationPresenter> implements
         Conversation.ConversationActionCallback,
         ClipboardHelper.ClipboardHelperCallback,
+        MediaButtonsHelper.MediaButtonsHelperCallback,
         ConversationView {
 
     public static final int REQ_ADD_CONTACT = 42;
@@ -467,4 +469,19 @@
                 .putExtra(KEY_CONTACT_RING_ID, contactRingId);
         startActivityForResult(intent, HomeActivity.REQUEST_CODE_CALL);
     }
+
+    @Override
+    public void positiveButtonClicked() {
+        presenter.clickOnGoingPane();
+    }
+
+    @Override
+    public void negativeButtonClicked() {
+        presenter.clickOnGoingPane();
+    }
+
+    @Override
+    public void toggleButtonClicked() {
+        presenter.clickOnGoingPane();
+    }
 }
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/utils/MediaButtonsHelper.java b/ring-android/app/src/main/java/cx/ring/utils/MediaButtonsHelper.java
new file mode 100644
index 0000000..6785a08
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/utils/MediaButtonsHelper.java
@@ -0,0 +1,54 @@
+package cx.ring.utils;
+
+
+import android.view.KeyEvent;
+
+public class MediaButtonsHelper {
+
+    public static boolean handleMediaKeyCode(int keyCode, MediaButtonsHelperCallback mediaButtonsHelperCallback) {
+        boolean isHandledKey = false;
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_CALL:
+            case KeyEvent.KEYCODE_MEDIA_PLAY:
+            case KeyEvent.KEYCODE_ENTER:
+            case KeyEvent.KEYCODE_BUTTON_A:
+            case KeyEvent.KEYCODE_HOME:
+                mediaButtonsHelperCallback.positiveButtonClicked();
+                isHandledKey = true;
+                break;
+            case KeyEvent.KEYCODE_ENDCALL:
+            case KeyEvent.KEYCODE_MEDIA_PAUSE:
+            case KeyEvent.KEYCODE_DEL:
+            case KeyEvent.KEYCODE_BUTTON_B:
+            case KeyEvent.KEYCODE_MEDIA_STOP:
+                mediaButtonsHelperCallback.negativeButtonClicked();
+                isHandledKey = true;
+                break;
+            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+            case KeyEvent.KEYCODE_HEADSETHOOK:
+                mediaButtonsHelperCallback.toggleButtonClicked();
+                isHandledKey = true;
+                break;
+        }
+        return isHandledKey;
+    }
+
+    /**
+     * Media buttons actions table:
+     * <table>
+     * <tr><th></th>                <th>positive btn</th>    <th>negative btn</th>	  <th>toggle btn</th></tr>
+     * <tr><th>conversation</th>	   <td>redirect</td>       <td>redirect</td>	    <td>redirect</td></tr>
+     * <tr><th>incoming call</th>      <td>accept</td>	       <td>refuse</td>          <td>/</td></tr>
+     * <tr><th>outgoing call</th>      <td>hangup</td>         <td>hangup</td>	        <td>hangup</td></tr>
+     * <tr><th>calling</th>	           <td>hangup</td>         <td>hangup</td>	        <td>hangup</td></tr>
+     * </table>
+     */
+
+    public interface MediaButtonsHelperCallback {
+        void positiveButtonClicked();
+
+        void negativeButtonClicked();
+
+        void toggleButtonClicked();
+    }
+}
diff --git a/ring-android/app/src/main/res/layout/activity_home.xml b/ring-android/app/src/main/res/layout/activity_home.xml
index 4622f2d..59a5df8 100644
--- a/ring-android/app/src/main/res/layout/activity_home.xml
+++ b/ring-android/app/src/main/res/layout/activity_home.xml
@@ -33,7 +33,8 @@
         android:id="@+id/content_frame"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:paddingLeft="@dimen/drawer_content_padding">
+        android:paddingEnd="0dp"
+        android:paddingStart="@dimen/drawer_content_padding">
 
         <android.support.v7.widget.Toolbar
             android:id="@+id/main_toolbar"
diff --git a/ring-android/libringclient/src/main/java/cx/ring/call/CallPresenter.java b/ring-android/libringclient/src/main/java/cx/ring/call/CallPresenter.java
index 9afbdd8..c9c56be 100644
--- a/ring-android/libringclient/src/main/java/cx/ring/call/CallPresenter.java
+++ b/ring-android/libringclient/src/main/java/cx/ring/call/CallPresenter.java
@@ -363,4 +363,26 @@
             }
         }
     }
+
+    public void positiveButtonClicked() {
+        if (mSipCall.isRinging() && mSipCall.isIncoming()) {
+            acceptCall();
+        } else {
+            hangupCall();
+        }
+    }
+
+    public void negativeButtonClicked() {
+        if (mSipCall.isRinging() && mSipCall.isIncoming()) {
+            refuseCall();
+        } else {
+            hangupCall();
+        }
+    }
+
+    public void toggleButtonClicked() {
+        if (!(mSipCall.isRinging() && mSipCall.isIncoming())) {
+            hangupCall();
+        }
+    }
 }