Merge "Improve fields displayed for reverse phone lookup" into klp-dev
diff --git a/InCallUI/res/drawable-hdpi/ic_end_call_holo_dark.png b/InCallUI/res/drawable-hdpi/ic_end_call_holo_dark.png
new file mode 100644
index 0000000..0f373bb
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/ic_end_call_holo_dark.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_split_holo_dark.png b/InCallUI/res/drawable-hdpi/ic_split_holo_dark.png
new file mode 100644
index 0000000..5b10cf9
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/ic_split_holo_dark.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_end_call_holo_dark.png b/InCallUI/res/drawable-mdpi/ic_end_call_holo_dark.png
new file mode 100644
index 0000000..e090cc8
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/ic_end_call_holo_dark.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_split_holo_dark.png b/InCallUI/res/drawable-mdpi/ic_split_holo_dark.png
new file mode 100644
index 0000000..3980acb
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/ic_split_holo_dark.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_end_call_holo_dark.png b/InCallUI/res/drawable-xhdpi/ic_end_call_holo_dark.png
new file mode 100644
index 0000000..f64f5e3
--- /dev/null
+++ b/InCallUI/res/drawable-xhdpi/ic_end_call_holo_dark.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_split_holo_dark.png b/InCallUI/res/drawable-xhdpi/ic_split_holo_dark.png
new file mode 100644
index 0000000..db558e4
--- /dev/null
+++ b/InCallUI/res/drawable-xhdpi/ic_split_holo_dark.png
Binary files differ
diff --git a/InCallUI/res/layout/call_button_fragment.xml b/InCallUI/res/layout/call_button_fragment.xml
index cec11f0..6a76a1c 100644
--- a/InCallUI/res/layout/call_button_fragment.xml
+++ b/InCallUI/res/layout/call_button_fragment.xml
@@ -42,10 +42,10 @@
          Most of the time this whole row is GONE.
          For now, at least, there's only ever one button visible here
          at a time, so this can be a simple FrameLayout. -->
-    <ViewStub android:id="@+id/extraButtonRow"
-              android:layout="@layout/extra_button_row"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"/>
+    <include android:id="@+id/extraButtonRow"
+          layout="@layout/extra_button_row"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"/>
 
     <!-- Row 2: The "End call" button. -->
     <ImageButton android:id="@+id/endButton"
diff --git a/InCallUI/res/layout/caller_in_conference.xml b/InCallUI/res/layout/caller_in_conference.xml
new file mode 100644
index 0000000..c2c68c8
--- /dev/null
+++ b/InCallUI/res/layout/caller_in_conference.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="64dp"
+    android:orientation="horizontal"
+    android:gravity="center_vertical">
+
+    <!-- Caller information -->
+    <LinearLayout
+        android:id="@+id/conferenceCallerSeparate"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_height="match_parent"
+        android:background="?android:attr/selectableItemBackground"
+        android:paddingStart="16dp"
+        android:orientation="horizontal"
+        android:gravity="center_vertical">
+
+        <!-- "Separate" (i.e. "go private") button for this caller -->
+        <ImageView
+            android:src="@drawable/ic_split_holo_dark"
+            android:layout_width="46dp"
+            android:layout_height="46dp"
+            android:scaleType="center"
+            android:contentDescription="@string/goPrivate"/>
+
+        <LinearLayout
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:paddingStart="8dp"
+             android:gravity="center_vertical"
+             android:orientation="vertical">
+
+            <!-- Name or number of this caller -->
+            <TextView
+                 android:id="@+id/conferenceCallerName"
+                 android:textAppearance="?android:attr/textAppearanceLarge"
+                 android:textSize="18sp"
+                 android:singleLine="true"
+                 android:layout_width="wrap_content"
+                 android:layout_height="match_parent"
+                 android:layout_marginEnd="2dp"
+                 />
+
+            <!-- Number of this caller if name is supplied above -->
+            <LinearLayout
+                 android:layout_width="wrap_content"
+                 android:layout_height="match_parent"
+                 android:orientation="horizontal"
+                 android:gravity="bottom">
+
+                <!-- Number -->
+                <TextView
+                     android:id="@+id/conferenceCallerNumber"
+                     android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:textSize="14sp"
+                     android:ellipsize="marquee"
+                     android:textColor="@color/manage_conference_secondary_text_color"
+                     android:singleLine="true"
+                     android:layout_marginEnd="8dp" />
+
+                <!-- Number type -->
+                <TextView
+                    android:id="@+id/conferenceCallerNumberType"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textSize="12sp"
+                    android:textColor="@color/manage_conference_secondary_text_color"
+                    android:ellipsize="marquee"
+                    android:singleLine="true"
+                    android:textAllCaps="true"
+                    android:gravity="start" />
+
+            </LinearLayout>  <!-- End of caller number -->
+
+        </LinearLayout>  <!-- End of caller information -->
+    </LinearLayout>
+
+    <!-- "End" button for this caller which should look like the
+         "end call" button in the main in-call UI screen -->
+    <ImageButton
+         android:id="@+id/conferenceCallerDisconnect"
+         android:layout_width="80dp"
+         android:layout_height="match_parent"
+         android:src="@drawable/ic_end_call_holo_dark"
+         android:background="@drawable/end_call_background"
+         android:scaleType="center"
+         android:contentDescription="@string/onscreenEndCallText" />
+
+</LinearLayout>  <!-- End of single list element -->
diff --git a/InCallUI/res/layout/conference_manager_fragment.xml b/InCallUI/res/layout/conference_manager_fragment.xml
new file mode 100644
index 0000000..f1fbfdf
--- /dev/null
+++ b/InCallUI/res/layout/conference_manager_fragment.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<!-- The "Manage conference" UI.  This panel is displayed (instead of
+     the inCallPanel) when the user clicks the "Manage conference"
+     button while on a conference call. -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:id="@+id/manageConferencePanel"
+                android:background="#FF000000"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:visibility="gone"
+                >
+    <!-- This original header (with timer) is currently not being used,
+         but may be of use in the future. -->
+    <!-- Header, including chronometer and List divider -->
+    <Chronometer
+        android:id="@+id/manageConferencePanelHeader"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:textSize="24sp"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        android:layout_alignParentTop="true"
+        android:layout_marginTop="5dip"
+        android:layout_marginBottom="5dip"
+        android:visibility="gone"/>
+
+    <ImageView
+        android:id="@+id/manageConferencePanelDivider"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:src="@android:drawable/divider_horizontal_dark"
+        android:scaleType="fitXY"
+        android:layout_below="@id/manageConferencePanelHeader"
+        android:visibility="gone"/>
+    <!-- End of the original header -->
+
+    <!-- Header which looks like ActionBar. -->
+    <FrameLayout
+        android:id="@+id/manageConferenceHeader"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@*android:drawable/ab_transparent_dark_holo">
+
+        <LinearLayout
+            android:id="@+id/manage_done"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="?android:attr/selectableItemBackground"
+            android:clickable="true"
+            android:orientation="horizontal">
+
+            <ImageView
+                android:src="?android:attr/homeAsUpIndicator"
+                android:layout_gravity="center_vertical|left"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="?android:attr/actionBarSize"
+                style="@*android:style/TextAppearance.Holo.Widget.ActionBar.Title"
+                android:gravity="center_vertical"
+                android:text="@string/manageConferenceLabel"/>
+        </LinearLayout>
+
+    </FrameLayout>  <!-- End of header -->
+
+    <!-- The scrollview wrapper for the list of callers on
+         the conference call (in case the list gets too long). -->
+    <ScrollView
+        android:id="@+id/conferenceList"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/manageConferenceHeader">
+
+        <!-- The actual list of callers; this embedded LinearLayout
+             required since scrollview only supports a single child. -->
+        <LinearLayout
+            android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:showDividers="middle|end"
+            android:divider="?android:attr/listDivider">
+
+            <!-- A conference can have at most MAX_CALLERS_IN_CONFERENCE (= 5) callers,
+                 so just define all those UI elements here. -->
+
+            <!-- Caller 0 -->
+            <include
+                layout="@layout/caller_in_conference"
+                android:id="@+id/caller0"/>
+
+            <!-- Caller 1 -->
+            <include
+                layout="@layout/caller_in_conference"
+                android:id="@+id/caller1"/>
+
+            <!-- Caller 2 -->
+            <include
+                layout="@layout/caller_in_conference"
+                android:id="@+id/caller2"/>
+
+            <!-- Caller 3 -->
+            <include
+                layout="@layout/caller_in_conference"
+                android:id="@+id/caller3"/>
+
+            <!-- Caller 4 -->
+            <include
+                layout="@layout/caller_in_conference"
+                android:id="@+id/caller4"/>
+
+        </LinearLayout>  <!-- End of "list of callers on conference call" -->
+
+    </ScrollView>  <!-- End of scrolling list wrapper for the linear layout -->
+
+</RelativeLayout>
diff --git a/InCallUI/res/layout/extra_button_row.xml b/InCallUI/res/layout/extra_button_row.xml
index 79c1136..a1600e8 100644
--- a/InCallUI/res/layout/extra_button_row.xml
+++ b/InCallUI/res/layout/extra_button_row.xml
@@ -32,7 +32,8 @@
         android:layout_height="wrap_content"
         android:paddingStart="@dimen/button_cluster_side_padding"
         android:paddingEnd="@dimen/button_cluster_side_padding"
-        android:background="?android:attr/selectableItemBackground">
+        android:background="?android:attr/selectableItemBackground"
+        android:visibility="gone">
         <!-- The entire LinearLayout here is clickable, so we don't
              care about clicks on the ImageButton itself. -->
         <ImageButton android:id="@+id/manageConferenceButtonImage"
@@ -55,7 +56,8 @@
         android:layout_height="wrap_content"
         android:paddingStart="@dimen/button_cluster_side_padding"
         android:paddingEnd="@dimen/button_cluster_side_padding"
-        android:background="?android:attr/selectableItemBackground">
+        android:background="?android:attr/selectableItemBackground"
+        android:visibility="gone">
         <!-- The entire LinearLayout here is clickable, so we don't
              care about clicks on the ImageButton itself. -->
         <ImageButton android:id="@+id/cdmaMergeButtonImage"
diff --git a/InCallUI/res/layout/incall_screen.xml b/InCallUI/res/layout/incall_screen.xml
index 86f67c9..892f917 100644
--- a/InCallUI/res/layout/incall_screen.xml
+++ b/InCallUI/res/layout/incall_screen.xml
@@ -67,4 +67,13 @@
               android:layout_marginBottom="@dimen/glowpadview_margin_bottom"
               android:visibility="gone" />
 
+    <fragment android:name="com.android.incallui.ConferenceManagerFragment"
+              android:id="@+id/conferenceManagerFragment"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:layout_alignParentTop="true"
+              android:layout_alignParentStart="true"
+              android:layout_alignParentBottom="true"
+              android:layout_alignParentEnd="true" />
+
 </FrameLayout>
diff --git a/InCallUI/res/values/strings.xml b/InCallUI/res/values/strings.xml
index 785e364..e7049e5 100755
--- a/InCallUI/res/values/strings.xml
+++ b/InCallUI/res/values/strings.xml
@@ -1467,4 +1467,7 @@
          related settings.
          [CHAR LIMIT=30] -->
     <string name="preference_category_ringtone">Ringtone &amp; Vibrate</string>
+
+    <!-- Label for "Manage conference call" panel [CHAR LIMIT=40] -->
+    <string name="manageConferenceLabel">Manage conference call</string>
 </resources>
diff --git a/InCallUI/src/com/android/incallui/AnswerPresenter.java b/InCallUI/src/com/android/incallui/AnswerPresenter.java
index 4c0f1d6..7f27133 100644
--- a/InCallUI/src/com/android/incallui/AnswerPresenter.java
+++ b/InCallUI/src/com/android/incallui/AnswerPresenter.java
@@ -127,12 +127,16 @@
     }
 
     public void onText() {
-        getUi().showMessageDialogue();
+        if (getUi() != null) {
+            getUi().showMessageDialogue();
+        }
     }
 
     public void rejectCallWithMessage(String message) {
         Log.d(this, "sendTextToDefaultActivity()...");
-        getUi().dismissPopup();
+        if (getUi() != null) {
+            getUi().dismissPopup();
+        }
         CallCommandClient.getInstance().rejectCall(mCallId, true, message);
     }
 
diff --git a/InCallUI/src/com/android/incallui/CallButtonFragment.java b/InCallUI/src/com/android/incallui/CallButtonFragment.java
index 1c3e737..4c42a78 100644
--- a/InCallUI/src/com/android/incallui/CallButtonFragment.java
+++ b/InCallUI/src/com/android/incallui/CallButtonFragment.java
@@ -23,6 +23,7 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewStub;
 import android.widget.CompoundButton;
 import android.widget.ImageButton;
 import android.widget.PopupMenu;
@@ -51,6 +52,9 @@
     private PopupMenu mAudioModePopup;
     private boolean mAudioModePopupVisible;
     private View mEndCallButton;
+    private View mExtraRowButton;
+    private View mManageConferenceButton;
+    private View mCDMAMergeButton;
 
     @Override
     CallButtonPresenter createPresenter() {
@@ -74,6 +78,17 @@
             Bundle savedInstanceState) {
         final View parent = inflater.inflate(R.layout.call_button_fragment, container, false);
 
+        mExtraRowButton = parent.findViewById(R.id.extraButtonRow);
+
+        mManageConferenceButton = parent.findViewById(R.id.manageConferenceButton);
+        mManageConferenceButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                getPresenter().manageConferenceButtonClicked();
+            }
+        });
+        mCDMAMergeButton = parent.findViewById(R.id.cdmaMergeButton);
+
         mEndCallButton = parent.findViewById(R.id.endButton);
         mEndCallButton.setOnClickListener(new View.OnClickListener() {
             @Override
@@ -449,4 +464,39 @@
             ((InCallActivity) getActivity()).displayDialpad(value);
         }
     }
+
+    @Override
+    public boolean isDialpadVisible() {
+        if (getActivity() != null && getActivity() instanceof InCallActivity) {
+            return ((InCallActivity) getActivity()).isDialpadVisible();
+        }
+        return false;
+    }
+
+    @Override
+    public void displayManageConferencePanel(boolean value) {
+        if (getActivity() != null && getActivity() instanceof InCallActivity) {
+            ((InCallActivity) getActivity()).displayManageConferencePanel(value);
+        }
+    }
+
+
+    @Override
+    public void showManageConferenceCallButton() {
+        mExtraRowButton.setVisibility(View.VISIBLE);
+        mManageConferenceButton.setVisibility(View.VISIBLE);
+        mCDMAMergeButton.setVisibility(View.GONE);
+    }
+
+    @Override
+    public void showCDMAMergeButton() {
+        mExtraRowButton.setVisibility(View.VISIBLE);
+        mManageConferenceButton.setVisibility(View.GONE);
+        mCDMAMergeButton.setVisibility(View.VISIBLE);
+    }
+
+    @Override
+    public void hideExtraRow() {
+       mExtraRowButton.setVisibility(View.GONE);
+    }
 }
diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
index bc9efe5..17273a8 100644
--- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
@@ -145,6 +145,10 @@
         CallCommandClient.getInstance().disconnectCall(mCall.getCallId());
     }
 
+    public void manageConferenceButtonClicked() {
+        getUi().displayManageConferencePanel(true);
+    }
+
     public void muteClicked(boolean checked) {
         Log.d(this, "turning on mute: " + checked);
 
@@ -217,6 +221,34 @@
                 ui.setMute(mPreviousMuteState);
                 mAutomaticallyMuted = false;
             }
+
+            // Finally, update the "extra button row": It's displayed above the
+            // "End" button, but only if necessary.  Also, it's never displayed
+            // while the dialpad is visible (since it would overlap.)
+            //
+            // The row contains two buttons:
+            //
+            // - "Manage conference" (used only on GSM devices)
+            // - "Merge" button (used only on CDMA devices)
+            // TODO(klp) Add cdma merge button
+            final boolean showCdmaMerge = false;
+//                    (phoneType == PhoneConstants.PHONE_TYPE_CDMA) && inCallControlState.canMerge;
+            final boolean showExtraButtonRow =
+                    (showCdmaMerge || call.isConferenceCall()) && !getUi().isDialpadVisible();
+            if (showExtraButtonRow) {
+                // Need to set up mCdmaMergeButton and mManageConferenceButton if this is the first
+                // time they're visible.
+                // TODO(klp) add cdma merge button
+//                if (mCdmaMergeButton == null) {
+//                    setupExtraButtons();
+//                }
+//                mCdmaMergeButton.setVisibility(showCdmaMerge ? View.VISIBLE : View.GONE);
+                if (call.isConferenceCall()) {
+                    getUi().showManageConferenceCallButton();
+                }
+            } else {
+                getUi().hideExtraRow();
+            }
         }
     }
 
@@ -241,7 +273,12 @@
         void showSwap(boolean show);
         void showAddCall(boolean show);
         void displayDialpad(boolean on);
+        boolean isDialpadVisible();
         void setAudio(int mode);
         void setSupportedAudio(int mask);
+        void showManageConferenceCallButton();
+        void showCDMAMergeButton();
+        void hideExtraRow();
+        void displayManageConferencePanel(boolean on);
     }
 }
diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java
index 486b083..9c772cb 100644
--- a/InCallUI/src/com/android/incallui/CallCardFragment.java
+++ b/InCallUI/src/com/android/incallui/CallCardFragment.java
@@ -185,6 +185,7 @@
         if (isConference) {
             name = getView().getResources().getString(R.string.card_title_conf_call);
             photo = getView().getResources().getDrawable(R.drawable.picture_conference);
+            nameIsNumber = false;
         }
 
         setPrimaryPhoneNumber(number);
@@ -203,9 +204,15 @@
 
     @Override
     public void setSecondary(boolean show, String name, boolean nameIsNumber, String label,
-            Drawable photo) {
+            Drawable photo, boolean isConference) {
 
         if (show) {
+            if (isConference) {
+                name = getView().getResources().getString(R.string.card_title_conf_call);
+                photo = getView().getResources().getDrawable(R.drawable.picture_conference);
+                nameIsNumber = false;
+            }
+
             showAndInitializeSecondaryCallInfo();
             mSecondaryCallName.setText(name);
 
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index 10eb561..ee58706 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -157,13 +157,13 @@
         if (secondary == null) {
             // Secondary call may have ended.  Update the ui.
             mSecondaryContactInfo = null;
-            updateSecondaryDisplayInfo();
+            updateSecondaryDisplayInfo(false);
         } else {
             if (mSecondary == null || mSecondary.getCallId() != secondary.getCallId()) {
                 // secondary call has changed
                 mSecondaryContactInfo = ContactInfoCache.buildCacheEntryFromCall(mContext,
                         secondary.getIdentification(), secondary.getState() == Call.State.INCOMING);
-                updateSecondaryDisplayInfo();
+                updateSecondaryDisplayInfo(secondary.isConferenceCall());
                 startContactInfoSearch(secondary.getIdentification(), false,
                         secondary.isConferenceCall(), secondary.getState() == Call.State.INCOMING);
             }
@@ -291,7 +291,7 @@
             updatePrimaryDisplayInfo(entry, isConference);
         } else {
             mSecondaryContactInfo = entry;
-            updateSecondaryDisplayInfo();
+            updateSecondaryDisplayInfo(isConference);
         }
     }
 
@@ -360,7 +360,7 @@
 
     }
 
-    private void updateSecondaryDisplayInfo() {
+    private void updateSecondaryDisplayInfo(boolean isConference) {
 
         final CallCardUi ui = getUi();
         if (ui == null) {
@@ -374,10 +374,10 @@
             final boolean nameIsNumber = nameForCall != null && nameForCall.equals(
                     mSecondaryContactInfo.number);
             ui.setSecondary(true, nameForCall, nameIsNumber, mSecondaryContactInfo.label,
-                    mSecondaryContactInfo.photo);
+                    mSecondaryContactInfo.photo, isConference);
         } else {
             // reset to nothing so that it starts off blank next time we use it.
-            ui.setSecondary(false, null, false, null, null);
+            ui.setSecondary(false, null, false, null, null, false);
         }
     }
 
@@ -506,7 +506,7 @@
         void setPrimary(String number, String name, boolean nameIsNumber, String label,
                 Drawable photo, boolean isConference, String gatewayLabel, String gatewayNumber);
         void setSecondary(boolean show, String name, boolean nameIsNumber, String label,
-                Drawable photo);
+                Drawable photo, boolean isConference);
         void setSecondaryImage(Bitmap bitmap);
         void setCallState(int state, Call.DisconnectCause cause, boolean bluetoothOn);
         void setPrimaryCallElapsedTime(boolean show, String duration);
diff --git a/InCallUI/src/com/android/incallui/CallCommandClient.java b/InCallUI/src/com/android/incallui/CallCommandClient.java
index 0cc7a8c..b59788b 100644
--- a/InCallUI/src/com/android/incallui/CallCommandClient.java
+++ b/InCallUI/src/com/android/incallui/CallCommandClient.java
@@ -79,7 +79,20 @@
         try {
             mCommandService.disconnectCall(callId);
         } catch (RemoteException e) {
-            Log.e(this, "Error answering call.", e);
+            Log.e(this, "Error disconnecting call.", e);
+        }
+    }
+
+    public void separateCall(int callId) {
+        Log.i(this, "separate Call: " + callId);
+        if (mCommandService == null) {
+            Log.e(this, "Cannot separate call; CallCommandService == null");
+            return;
+        }
+        try {
+            mCommandService.separateCall(callId);
+        } catch (RemoteException e) {
+            Log.e(this, "Error separating call.", e);
         }
     }
 
diff --git a/InCallUI/src/com/android/incallui/ConferenceManagerFragment.java b/InCallUI/src/com/android/incallui/ConferenceManagerFragment.java
new file mode 100644
index 0000000..75ff176
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/ConferenceManagerFragment.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2013 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;
+
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Chronometer;
+import android.widget.TextView;
+
+/**
+ * Fragment for call control buttons
+ */
+public class ConferenceManagerFragment
+        extends BaseFragment<ConferenceManagerPresenter,
+                ConferenceManagerPresenter.ConferenceManagerUi>
+        implements ConferenceManagerPresenter.ConferenceManagerUi {
+
+    private View mButtonManageConferenceDone;
+    private ViewGroup[] mConferenceCallList;
+    private Chronometer mConferenceTime;
+
+    @Override
+    ConferenceManagerPresenter createPresenter() {
+        // having a singleton instance.
+        return new ConferenceManagerPresenter();
+    }
+
+    @Override
+    ConferenceManagerPresenter.ConferenceManagerUi getUi() {
+        return this;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        final View parent = inflater.inflate(R.layout.conference_manager_fragment, container,
+                false);
+
+        // set up the Conference Call chronometer
+        mConferenceTime = (Chronometer) parent.findViewById(R.id.manageConferencePanelHeader);
+        mConferenceTime.setFormat(getActivity().getString(R.string.caller_manage_header));
+
+        // Create list of conference call widgets
+        mConferenceCallList = new ViewGroup[getPresenter().getMaxCallersInConference()];
+
+        final int[] viewGroupIdList = { R.id.caller0, R.id.caller1, R.id.caller2,
+                                        R.id.caller3, R.id.caller4 };
+        for (int i = 0; i < getPresenter().getMaxCallersInConference(); i++) {
+            mConferenceCallList[i] =
+                    (ViewGroup) parent.findViewById(viewGroupIdList[i]);
+        }
+
+        mButtonManageConferenceDone = parent.findViewById(R.id.manage_done);
+        mButtonManageConferenceDone.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                getPresenter().manageConferenceDoneClicked();
+            }
+        });
+
+        return parent;
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+    }
+
+    @Override
+    public void setVisible(boolean on) {
+        if (on) {
+            final CallList calls = CallList.getInstance();
+            getPresenter().init(getActivity(), calls);
+            getView().setVisibility(View.VISIBLE);
+
+        } else {
+            getView().setVisibility(View.GONE);
+        }
+    }
+
+    @Override
+    public boolean isFragmentVisible() {
+        return isVisible();
+    }
+
+    @Override
+    public void setRowVisible(int rowId, boolean on) {
+        if (on) {
+            mConferenceCallList[rowId].setVisibility(View.VISIBLE);
+        } else {
+            mConferenceCallList[rowId].setVisibility(View.GONE);
+        }
+    }
+
+    /**
+     * Helper function to fill out the Conference Call(er) information
+     * for each item in the "Manage Conference Call" list.
+     */
+    @Override
+    public final void displayCallerInfoForConferenceRow(int rowId, String callerName,
+            String callerNumber, String callerNumberType) {
+
+        final TextView nameTextView = (TextView) mConferenceCallList[rowId].findViewById(
+                R.id.conferenceCallerName);
+        final TextView numberTextView = (TextView) mConferenceCallList[rowId].findViewById(
+                R.id.conferenceCallerNumber);
+        final TextView numberTypeTextView = (TextView) mConferenceCallList[rowId].findViewById(
+                R.id.conferenceCallerNumberType);
+
+        // set the caller name
+        nameTextView.setText(callerName);
+
+        // set the caller number in subscript, or make the field disappear.
+        if (TextUtils.isEmpty(callerNumber)) {
+            numberTextView.setVisibility(View.GONE);
+            numberTypeTextView.setVisibility(View.GONE);
+        } else {
+            numberTextView.setVisibility(View.VISIBLE);
+            numberTextView.setText(callerNumber);
+            numberTypeTextView.setVisibility(View.VISIBLE);
+            numberTypeTextView.setText(callerNumberType);
+        }
+    }
+
+    @Override
+    public final void setupEndButtonForRow(final int rowId) {
+        View endButton = mConferenceCallList[rowId].findViewById(R.id.conferenceCallerDisconnect);
+        endButton.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    getPresenter().endConferenceConnection(rowId);
+                }
+        });
+    }
+
+    @Override
+    public final void setCanSeparateButtonForRow(final int rowId, boolean canSeparate) {
+        final View separateButton = mConferenceCallList[rowId].findViewById(
+                R.id.conferenceCallerSeparate);
+
+        if (canSeparate) {
+            final View.OnClickListener separateThisConnection = new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        getPresenter().separateConferenceConnection(rowId);
+                    }
+                };
+            separateButton.setOnClickListener(separateThisConnection);
+            separateButton.setVisibility(View.VISIBLE);
+        } else {
+            separateButton.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    /**
+     * Starts the "conference time" chronometer.
+     */
+    @Override
+    public void startConferenceTime(long base) {
+        if (mConferenceTime != null) {
+            mConferenceTime.setBase(base);
+            mConferenceTime.start();
+        }
+    }
+
+    /**
+     * Stops the "conference time" chronometer.
+     */
+    @Override
+    public void stopConferenceTime() {
+        if (mConferenceTime != null) {
+            mConferenceTime.stop();
+        }
+    }
+}
\ No newline at end of file
diff --git a/InCallUI/src/com/android/incallui/ConferenceManagerPresenter.java b/InCallUI/src/com/android/incallui/ConferenceManagerPresenter.java
new file mode 100644
index 0000000..6b6f7d8
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/ConferenceManagerPresenter.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2013 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;
+
+import android.content.Context;
+
+import com.android.incallui.ContactInfoCache.ContactCacheEntry;
+import com.android.incallui.InCallPresenter.InCallState;
+import com.android.incallui.InCallPresenter.InCallStateListener;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSortedSet;
+
+/**
+ * Logic for call buttons.
+ */
+public class ConferenceManagerPresenter
+        extends Presenter<ConferenceManagerPresenter.ConferenceManagerUi>
+        implements InCallStateListener {
+
+    private static final int MAX_CALLERS_IN_CONFERENCE = 5;
+
+    private int mNumCallersInConference;
+    private Integer[] mCallerIds;
+    private Context mContext;
+
+    @Override
+    public void onStateChange(InCallState state, CallList callList) {
+        if (getUi().isFragmentVisible()) {
+            if (state == InCallState.INCALL && callList.getActiveOrBackgroundCall() != null &&
+                    callList.getActiveOrBackgroundCall().isConferenceCall()) {
+                Log.v(this, "Number of existing calls is " +
+                        String.valueOf(callList.getActiveCall().getChildCallIds().size()));
+                update(callList);
+            } else {
+                getUi().setVisible(false);
+            }
+        }
+    }
+
+    public void init(Context context, CallList callList) {
+        mContext = Preconditions.checkNotNull(context);
+        mContext = context;
+        update(callList);
+    }
+
+    private void update(CallList callList) {
+        mCallerIds = null;
+        mCallerIds = callList.getActiveCall().getChildCallIds().toArray(new Integer[0]);
+        mNumCallersInConference = mCallerIds.length;
+        Log.v(this, "Number of calls is " + String.valueOf(mNumCallersInConference));
+
+        // Users can split out a call from the conference call if there either the active call
+        // or the holding call is empty. If both are filled at the moment, users can not split out
+        // another call.
+        final boolean hasActiveCall = (callList.getActiveCall() != null);
+        final boolean hasHoldingCall = (callList.getBackgroundCall() != null);
+        boolean canSeparate = !(hasActiveCall && hasHoldingCall);
+
+        for (int i = 0; i < MAX_CALLERS_IN_CONFERENCE; i++) {
+            if (i < mNumCallersInConference) {
+                // Fill in the row in the UI for this caller.
+
+                final ContactCacheEntry contactCache = ContactInfoCache.getInstance(mContext).
+                        getInfo(mCallerIds[i]);
+                updateManageConferenceRow(i, contactCache, canSeparate);
+            } else {
+                // Blank out this row in the UI
+                updateManageConferenceRow(i, null, false);
+            }
+        }
+    }
+
+    /**
+      * Updates a single row of the "Manage conference" UI.  (One row in this
+      * UI represents a single caller in the conference.)
+      *
+      * @param i the row to update
+      * @param contactCacheEntry the contact details corresponding to this caller.
+      *        If null, that means this is an "empty slot" in the conference,
+      *        so hide this row in the UI.
+      * @param canSeparate if true, show a "Separate" (i.e. "Private") button
+      *        on this row in the UI.
+      */
+    public void updateManageConferenceRow(final int i,
+                                          final ContactCacheEntry contactCacheEntry,
+                                          boolean canSeparate) {
+
+        if (contactCacheEntry != null) {
+            // Activate this row of the Manage conference panel:
+            getUi().setRowVisible(i, true);
+
+            final String name = contactCacheEntry.name;
+            final String number = contactCacheEntry.number;
+
+            if (canSeparate) {
+                getUi().setCanSeparateButtonForRow(i, canSeparate);
+            }
+            // display the CallerInfo.
+            getUi().setupEndButtonForRow(i);
+            getUi().displayCallerInfoForConferenceRow(i, name, number, contactCacheEntry.label);
+        } else {
+            // Disable this row of the Manage conference panel:
+            getUi().setRowVisible(i, false);
+        }
+    }
+
+    public void manageConferenceDoneClicked() {
+        getUi().setVisible(false);
+    }
+
+    public int getMaxCallersInConference() {
+        return MAX_CALLERS_IN_CONFERENCE;
+    }
+
+    public void separateConferenceConnection(int rowId) {
+        CallCommandClient.getInstance().separateCall(mCallerIds[rowId]);
+    }
+
+    public void endConferenceConnection(int rowId) {
+        CallCommandClient.getInstance().disconnectCall(mCallerIds[rowId]);
+    }
+
+    public interface ConferenceManagerUi extends Ui {
+        void setVisible(boolean on);
+        boolean isFragmentVisible();
+        void setRowVisible(int rowId, boolean on);
+        void displayCallerInfoForConferenceRow(int rowId, String callerName, String callerNumber,
+                String callerNumberType);
+        void setCanSeparateButtonForRow(int rowId, boolean canSeparate);
+        void setupEndButtonForRow(int rowId);
+        void startConferenceTime(long base);
+        void stopConferenceTime();
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java
index fee4d95..6cae6b0 100644
--- a/InCallUI/src/com/android/incallui/InCallActivity.java
+++ b/InCallUI/src/com/android/incallui/InCallActivity.java
@@ -34,6 +34,7 @@
     private CallCardFragment mCallCardFragment;
     private AnswerFragment mAnswerFragment;
     private DialpadFragment mDialpadFragment;
+    private ConferenceManagerFragment mConferenceManagerFragment;
     private boolean mIsForegroundActivity;
 
     @Override
@@ -287,6 +288,11 @@
             mDialpadFragment.getView().setVisibility(View.INVISIBLE);
         }
 
+        if (mConferenceManagerFragment == null) {
+            mConferenceManagerFragment = (ConferenceManagerFragment) getFragmentManager()
+                    .findFragmentById(R.id.conferenceManagerFragment);
+            mConferenceManagerFragment.getView().setVisibility(View.INVISIBLE);
+        }
         setUpPresenterCallbacks();
     }
 
@@ -302,6 +308,7 @@
 
         mainPresenter.addListener(mCallButtonFragment.getPresenter());
         mainPresenter.addListener(mCallCardFragment.getPresenter());
+        mainPresenter.addListener(mConferenceManagerFragment.getPresenter());
 
         // setting activity should be last thing in setup process
         mainPresenter.setActivity(this);
@@ -313,6 +320,7 @@
 
         mainPresenter.removeListener(mCallButtonFragment.getPresenter());
         mainPresenter.removeListener(mCallCardFragment.getPresenter());
+        mainPresenter.removeListener(mConferenceManagerFragment.getPresenter());
 
         mainPresenter.setActivity(null);
     }
@@ -332,4 +340,14 @@
             mCallCardFragment.setVisible(true);
         }
     }
+
+    public boolean isDialpadVisible() {
+        return mDialpadFragment.isVisible();
+    }
+
+    public void displayManageConferencePanel(boolean showPanel) {
+        if (showPanel) {
+            mConferenceManagerFragment.setVisible(true);
+        }
+    }
 }