Add video handoff button to InCallUI

This CL adds a video handoff button to the InCallUI. The actual
functionality will be implemented in t Google Dialer using the
AuxiliaryActionService binding.

Bug: 10929230

Change-Id: Ie44aef5160be388ec19b5239d71faa7297080c6e
diff --git a/InCallUI/res/layout/call_button_fragment.xml b/InCallUI/res/layout/call_button_fragment.xml
index 013d1b2..c0d9364 100644
--- a/InCallUI/res/layout/call_button_fragment.xml
+++ b/InCallUI/res/layout/call_button_fragment.xml
@@ -164,7 +164,18 @@
                      android:visibility="gone"
                 />
 
-        <!-- Separator between 4th (or 5th) button and right padding -->
+        <!-- Separator between 5th and 6th button -->
+        <View android:id="@+id/auxiliaryActionSpacer"
+              style="@style/VerticalSeparator"
+              android:visibility="gone"
+                />
+
+        <ImageButton android:id="@+id/auxiliaryActionButton"
+                     style="@style/InCallButton"
+                     android:visibility="gone"
+                />
+
+        <!-- Separator between last button and right padding -->
         <View style="@style/VerticalSeparator"/>
 
     </LinearLayout>
diff --git a/InCallUI/src/com/android/incallui/CallButtonFragment.java b/InCallUI/src/com/android/incallui/CallButtonFragment.java
index ed76903..a63b325 100644
--- a/InCallUI/src/com/android/incallui/CallButtonFragment.java
+++ b/InCallUI/src/com/android/incallui/CallButtonFragment.java
@@ -16,6 +16,7 @@
 
 package com.android.incallui;
 
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.os.Bundle;
 import android.view.LayoutInflater;
@@ -48,6 +49,8 @@
     private ImageButton mMergeButton;
     private ImageButton mAddCallButton;
     private ImageButton mSwapButton;
+    private ImageButton mAuxiliaryActionButton;
+    private View mAuxiliaryActionSpacer;
 
     private PopupMenu mAudioModePopup;
     private boolean mAudioModePopupVisible;
@@ -141,6 +144,9 @@
         mMergeButton.setOnClickListener(this);
         mSwapButton = (ImageButton) parent.findViewById(R.id.swapButton);
         mSwapButton.setOnClickListener(this);
+        mAuxiliaryActionButton = (ImageButton) parent.findViewById(R.id.auxiliaryActionButton);
+        mAuxiliaryActionButton.setOnClickListener(this);
+        mAuxiliaryActionSpacer = parent.findViewById(R.id.auxiliaryActionSpacer);
 
         return parent;
     }
@@ -183,6 +189,9 @@
             case R.id.dialpadButton:
                 getPresenter().showDialpadClicked(mShowDialpadButton.isChecked());
                 break;
+            case R.id.auxiliaryActionButton:
+                getPresenter().auxiliaryActionButtonClicked();
+                break;
             default:
                 Log.wtf(this, "onClick: unexpected");
                 break;
@@ -526,6 +535,15 @@
         }
     }
 
+    @Override
+    public void updateAuxiliaryActionButton(boolean show, String description, Drawable drawable) {
+        mAuxiliaryActionButton.setVisibility(show ? View.VISIBLE : View.GONE);
+        mAuxiliaryActionSpacer.setVisibility(show ? View.VISIBLE : View.GONE);
+        if (show) {
+            mAuxiliaryActionButton.setContentDescription(description);
+            mAuxiliaryActionButton.setImageDrawable(drawable);
+        }
+    }
 
     @Override
     public void showManageConferenceCallButton() {
diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
index 67a642b..104da6d 100644
--- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
@@ -16,12 +16,17 @@
 
 package com.android.incallui;
 
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
 import com.android.contacts.common.util.PhoneNumberHelper;
 import com.android.contacts.common.util.TelephonyManagerUtils;
 import com.android.incallui.AudioModeProvider.AudioModeListener;
 import com.android.incallui.InCallPresenter.InCallState;
 import com.android.incallui.InCallPresenter.InCallStateListener;
 import com.android.incallui.InCallPresenter.IncomingCallListener;
+import com.android.incallui.service.AuxiliaryActionService;
+import com.android.incalluibind.ServiceFactory;
 import com.android.services.telephony.common.AudioMode;
 import com.android.services.telephony.common.Call;
 import com.android.services.telephony.common.Call.Capabilities;
@@ -32,7 +37,8 @@
  * Logic for call buttons.
  */
 public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButtonUi>
-        implements InCallStateListener, AudioModeListener, IncomingCallListener {
+        implements InCallStateListener, AudioModeListener, IncomingCallListener,
+        AuxiliaryActionService.Client {
 
     private Call mCall;
     private boolean mAutomaticallyMuted = false;
@@ -43,6 +49,8 @@
 
     private InCallState mPreviousState = null;
 
+    private AuxiliaryActionService mAuxiliaryActionService = null;
+
     public CallButtonPresenter() {
     }
 
@@ -55,6 +63,12 @@
         // register for call state changes last
         InCallPresenter.getInstance().addListener(this);
         InCallPresenter.getInstance().addIncomingCallListener(this);
+
+        Context context = ((Fragment) ui).getActivity();
+        mAuxiliaryActionService = ServiceFactory.newAuxiliaryActionService(context);
+        if (mAuxiliaryActionService != null) {
+            mAuxiliaryActionService.setClient(this);
+        }
     }
 
     @Override
@@ -64,6 +78,9 @@
         InCallPresenter.getInstance().removeListener(this);
         AudioModeProvider.getInstance().removeListener(this);
         InCallPresenter.getInstance().removeIncomingCallListener(this);
+        if (mAuxiliaryActionService != null) {
+            mAuxiliaryActionService.setClient(null);
+        }
     }
 
     @Override
@@ -126,6 +143,11 @@
         }
     }
 
+    @Override
+    public void onAuxiliaryActionStateChanged() {
+        updateAuxiliaryActionButton();
+    }
+
     public int getAudioMode() {
         return AudioModeProvider.getInstance().getAudioMode();
     }
@@ -219,6 +241,10 @@
         updateExtraButtonRow();
     }
 
+    public void auxiliaryActionButtonClicked() {
+        mAuxiliaryActionService.performAction();
+    }
+
     private void updateUi(InCallState state, Call call) {
         final CallButtonUi ui = getUi();
         if (ui == null) {
@@ -305,6 +331,7 @@
             mShowManageConference = (call.isConferenceCall() && !isGenericConference);
 
             updateExtraButtonRow();
+            updateAuxiliaryActionButton();
         }
     }
 
@@ -326,6 +353,22 @@
         }
     }
 
+    private void updateAuxiliaryActionButton() {
+        if (mAuxiliaryActionService == null) {
+            return;
+        }
+        final CallButtonUi ui = getUi();
+        if (ui == null) {
+            return;
+        }
+        if (mCall != null) {
+            mAuxiliaryActionService.setRemotePhoneNumber(mCall.getNumber());
+        }
+        ui.updateAuxiliaryActionButton(mAuxiliaryActionService.isActionEnabled(),
+                mAuxiliaryActionService.getActionDescription(),
+                mAuxiliaryActionService.getActionDrawable());
+    }
+
     public void refreshMuteState() {
         // Restore the previous mute state
         if (mAutomaticallyMuted &&
@@ -357,5 +400,6 @@
         void showGenericMergeButton();
         void hideExtraRow();
         void displayManageConferencePanel(boolean on);
+        void updateAuxiliaryActionButton(boolean show, String description, Drawable drawable);
     }
 }
diff --git a/InCallUI/src/com/android/incallui/service/AuxiliaryActionService.java b/InCallUI/src/com/android/incallui/service/AuxiliaryActionService.java
new file mode 100644
index 0000000..b0970fa
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/service/AuxiliaryActionService.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.incallui.service;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Generic service that allows the user to perform an action from within the in call UI.
+ * If the service is implemented then a button is added to the InCallUI. The button is visible if
+ * AuxiliaryActionService.isActionEnabled() returns true and hidden otherwise. If this service
+ * is not implemented then the button is always hidden.
+ */
+public interface AuxiliaryActionService {
+    /**
+     * Client of the service.
+     */
+    public interface Client {
+        /**
+         * Called when the action's enabled state may have changed.
+         */
+        public void onAuxiliaryActionStateChanged();
+    }
+
+    /**
+     * Sets the client.
+     */
+    public void setClient(Client client);
+
+    /**
+     * Sets the remote phone number.
+     */
+    public void setRemotePhoneNumber(String remotePhoneNumber);
+
+    /**
+     * Gets the action's description.
+     *
+     * @return the description.
+     */
+    public String getActionDescription();
+
+    /**
+     * Gets the action's drawable.
+     *
+     * @return the drawable.
+     */
+    public Drawable getActionDrawable();
+
+    /**
+     * Checks if the auxiliary action is enabled.
+     *
+     * @return true if the action is enabled, otherwise false.
+     */
+    public boolean isActionEnabled();
+
+    /**
+     * Triggers the action for the auxiliary service.
+     */
+    public void performAction();
+}
diff --git a/InCallUI/src/com/android/incalluibind/ServiceFactory.java b/InCallUI/src/com/android/incalluibind/ServiceFactory.java
index 7191f14..fcbc0ca 100644
--- a/InCallUI/src/com/android/incalluibind/ServiceFactory.java
+++ b/InCallUI/src/com/android/incalluibind/ServiceFactory.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 
+import com.android.incallui.service.AuxiliaryActionService;
 import com.android.incallui.service.PhoneNumberService;
 
 /**
@@ -29,4 +30,9 @@
         // no phone number service.
         return null;
     }
+
+    public static AuxiliaryActionService newAuxiliaryActionService(Context context) {
+        // no auxiliary action service.
+        return null;
+    }
 }