| /* | 
 |  * 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. | 
 |  */ | 
 |  | 
 | package com.android.phone; | 
 |  | 
 | import android.os.SystemProperties; | 
 | import android.text.TextUtils; | 
 | import android.util.Log; | 
 | import android.view.View; | 
 | import android.view.ViewGroup; | 
 | import android.view.ViewStub; | 
 | import android.widget.Chronometer; | 
 | import android.widget.TextView; | 
 |  | 
 | import com.android.internal.telephony.CallerInfo; | 
 | import com.android.internal.telephony.CallerInfoAsyncQuery; | 
 | import com.android.internal.telephony.CallManager; | 
 | import com.android.internal.telephony.Connection; | 
 |  | 
 | import java.util.List; | 
 |  | 
 |  | 
 | /** | 
 |  * Helper class to initialize and run the InCallScreen's "Manage conference" UI. | 
 |  */ | 
 | public class ManageConferenceUtils { | 
 |     private static final String LOG_TAG = "ManageConferenceUtils"; | 
 |     private static final boolean DBG = | 
 |             (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1); | 
 |  | 
 |     /** | 
 |      * CallerInfoAsyncQuery.OnQueryCompleteListener implementation. | 
 |      * | 
 |      * This object listens for results from the caller-id info queries we | 
 |      * fire off in updateManageConferenceRow(), and updates the | 
 |      * corresponding conference row. | 
 |      */ | 
 |     private final class QueryCompleteListener | 
 |             implements CallerInfoAsyncQuery.OnQueryCompleteListener { | 
 |         private final int mConferencCallListIndex; | 
 |  | 
 |         public QueryCompleteListener(int index) { | 
 |             mConferencCallListIndex = index; | 
 |         } | 
 |  | 
 |         @Override | 
 |         public void onQueryComplete(int token, Object cookie, CallerInfo ci) { | 
 |             if (DBG) log("callerinfo query complete, updating UI." + ci); | 
 |  | 
 |             Connection connection = (Connection) cookie; | 
 |             int presentation = connection.getNumberPresentation(); | 
 |  | 
 |             // get the viewgroup (conference call list item) and make it visible | 
 |             ViewGroup viewGroup = mConferenceCallList[mConferencCallListIndex]; | 
 |             viewGroup.setVisibility(View.VISIBLE); | 
 |  | 
 |             // update the list item with this information. | 
 |             displayCallerInfoForConferenceRow(ci, presentation, | 
 |                     (TextView) viewGroup.findViewById(R.id.conferenceCallerName), | 
 |                     (TextView) viewGroup.findViewById(R.id.conferenceCallerNumberType), | 
 |                     (TextView) viewGroup.findViewById(R.id.conferenceCallerNumber)); | 
 |         } | 
 |     } | 
 |  | 
 |     private InCallScreen mInCallScreen; | 
 |     private CallManager mCM; | 
 |  | 
 |     // "Manage conference" UI elements and state | 
 |     private ViewGroup mManageConferencePanel; | 
 |     private View mButtonManageConferenceDone; | 
 |     private ViewGroup[] mConferenceCallList; | 
 |     private int mNumCallersInConference; | 
 |     private Chronometer mConferenceTime; | 
 |  | 
 |     // See CallTracker.MAX_CONNECTIONS_PER_CALL | 
 |     private static final int MAX_CALLERS_IN_CONFERENCE = 5; | 
 |  | 
 |     public ManageConferenceUtils(InCallScreen inCallScreen, CallManager cm) { | 
 |         if (DBG) log("ManageConferenceUtils constructor..."); | 
 |         mInCallScreen = inCallScreen; | 
 |         mCM = cm; | 
 |     } | 
 |  | 
 |     public void initManageConferencePanel() { | 
 |         if (DBG) log("initManageConferencePanel()..."); | 
 |         if (mManageConferencePanel == null) { | 
 |             if (DBG) log("initManageConferencePanel: first-time initialization!"); | 
 |  | 
 |             // Inflate the ViewStub, look up and initialize the UI elements. | 
 |             ViewStub stub = (ViewStub) mInCallScreen.findViewById(R.id.manageConferencePanelStub); | 
 |             stub.inflate(); | 
 |  | 
 |             mManageConferencePanel = | 
 |                     (ViewGroup) mInCallScreen.findViewById(R.id.manageConferencePanel); | 
 |             if (mManageConferencePanel == null) { | 
 |                 throw new IllegalStateException("Couldn't find manageConferencePanel!"); | 
 |             } | 
 |  | 
 |             // set up the Conference Call chronometer | 
 |             mConferenceTime = | 
 |                     (Chronometer) mInCallScreen.findViewById(R.id.manageConferencePanelHeader); | 
 |             mConferenceTime.setFormat(mInCallScreen.getString(R.string.caller_manage_header)); | 
 |  | 
 |             // Create list of conference call widgets | 
 |             mConferenceCallList = new ViewGroup[MAX_CALLERS_IN_CONFERENCE]; | 
 |  | 
 |             final int[] viewGroupIdList = { R.id.caller0, R.id.caller1, R.id.caller2, | 
 |                                             R.id.caller3, R.id.caller4 }; | 
 |             for (int i = 0; i < MAX_CALLERS_IN_CONFERENCE; i++) { | 
 |                 mConferenceCallList[i] = | 
 |                         (ViewGroup) mInCallScreen.findViewById(viewGroupIdList[i]); | 
 |             } | 
 |  | 
 |             mButtonManageConferenceDone = mInCallScreen.findViewById(R.id.manage_done); | 
 |             mButtonManageConferenceDone.setOnClickListener(mInCallScreen); | 
 |         } | 
 |     } | 
 |  | 
 |     /** | 
 |      * Shows or hides the manageConferencePanel. | 
 |      */ | 
 |     public void setPanelVisible(boolean visible) { | 
 |         if (mManageConferencePanel != null) { | 
 |             mManageConferencePanel.setVisibility(visible ? View.VISIBLE : View.GONE); | 
 |         } | 
 |     } | 
 |  | 
 |     /** | 
 |      * Starts the "conference time" chronometer. | 
 |      */ | 
 |     public void startConferenceTime(long base) { | 
 |         if (mConferenceTime != null) { | 
 |             mConferenceTime.setBase(base); | 
 |             mConferenceTime.start(); | 
 |         } | 
 |     } | 
 |  | 
 |     /** | 
 |      * Stops the "conference time" chronometer. | 
 |      */ | 
 |     public void stopConferenceTime() { | 
 |         if (mConferenceTime != null) { | 
 |             mConferenceTime.stop(); | 
 |         } | 
 |     } | 
 |  | 
 |     public int getNumCallersInConference() { | 
 |         return mNumCallersInConference; | 
 |     } | 
 |  | 
 |     /** | 
 |      * Updates the "Manage conference" UI based on the specified List of | 
 |      * connections. | 
 |      * | 
 |      * @param connections the List of connections belonging to | 
 |      *        the current foreground call; size must be greater than 1 | 
 |      *        (or it wouldn't be a conference call in the first place.) | 
 |      */ | 
 |     public void updateManageConferencePanel(List<Connection> connections) { | 
 |         mNumCallersInConference = connections.size(); | 
 |         if (DBG) log("updateManageConferencePanel()... num connections in conference = " | 
 |                       + mNumCallersInConference); | 
 |  | 
 |         // Can we give the user the option to separate out ("go private with") a single | 
 |         // caller from this conference? | 
 |         final boolean hasActiveCall = mCM.hasActiveFgCall(); | 
 |         final boolean hasHoldingCall = mCM.hasActiveBgCall(); | 
 |         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. | 
 |                 Connection connection = (Connection) connections.get(i); | 
 |                 updateManageConferenceRow(i, connection, 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 connection the Connection 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 Connection connection, | 
 |                                           boolean canSeparate) { | 
 |         if (DBG) log("updateManageConferenceRow(" + i + ")...  connection = " + connection); | 
 |  | 
 |         if (connection != null) { | 
 |             // Activate this row of the Manage conference panel: | 
 |             mConferenceCallList[i].setVisibility(View.VISIBLE); | 
 |  | 
 |             // get the relevant children views | 
 |             View endButton = mConferenceCallList[i].findViewById(R.id.conferenceCallerDisconnect); | 
 |             View separateButton = mConferenceCallList[i].findViewById( | 
 |                     R.id.conferenceCallerSeparate); | 
 |             TextView nameTextView = (TextView) mConferenceCallList[i].findViewById( | 
 |                     R.id.conferenceCallerName); | 
 |             TextView numberTextView = (TextView) mConferenceCallList[i].findViewById( | 
 |                     R.id.conferenceCallerNumber); | 
 |             TextView numberTypeTextView = (TextView) mConferenceCallList[i].findViewById( | 
 |                     R.id.conferenceCallerNumberType); | 
 |  | 
 |             if (DBG) log("- button: " + endButton + ", nameTextView: " + nameTextView); | 
 |  | 
 |             // Hook up this row's buttons. | 
 |             View.OnClickListener endThisConnection = new View.OnClickListener() { | 
 |                     @Override | 
 |                     public void onClick(View v) { | 
 |                         endConferenceConnection(i, connection); | 
 |                         PhoneGlobals.getInstance().pokeUserActivity(); | 
 |                     } | 
 |                 }; | 
 |             endButton.setOnClickListener(endThisConnection); | 
 |             // | 
 |             if (canSeparate) { | 
 |                 View.OnClickListener separateThisConnection = new View.OnClickListener() { | 
 |                         @Override | 
 |                         public void onClick(View v) { | 
 |                             separateConferenceConnection(i, connection); | 
 |                             PhoneGlobals.getInstance().pokeUserActivity(); | 
 |                         } | 
 |                     }; | 
 |                 separateButton.setOnClickListener(separateThisConnection); | 
 |                 separateButton.setVisibility(View.VISIBLE); | 
 |             } else { | 
 |                 separateButton.setVisibility(View.INVISIBLE); | 
 |             } | 
 |  | 
 |             // Name/number for this caller. | 
 |             QueryCompleteListener listener = new QueryCompleteListener(i); | 
 |             PhoneUtils.CallerInfoToken info = | 
 |                     PhoneUtils.startGetCallerInfo(mInCallScreen, | 
 |                             connection, listener, connection); | 
 |             if (DBG) log("  - got info from startGetCallerInfo(): " + info); | 
 |  | 
 |             // display the CallerInfo. | 
 |             displayCallerInfoForConferenceRow(info.currentInfo, connection.getNumberPresentation(), | 
 |                     nameTextView, numberTypeTextView, numberTextView); | 
 |         } else { | 
 |             // Disable this row of the Manage conference panel: | 
 |             mConferenceCallList[i].setVisibility(View.GONE); | 
 |         } | 
 |     } | 
 |  | 
 |     /** | 
 |      * Helper function to fill out the Conference Call(er) information | 
 |      * for each item in the "Manage Conference Call" list. | 
 |      * | 
 |      * @param presentation presentation specified by {@link Connection}. | 
 |      */ | 
 |     public final void displayCallerInfoForConferenceRow(CallerInfo ci, int presentation, | 
 |             TextView nameTextView, TextView numberTypeTextView, TextView numberTextView) { | 
 |         // gather the correct name and number information. | 
 |         String callerName = ""; | 
 |         String callerNumber = ""; | 
 |         String callerNumberType = ""; | 
 |         if (ci != null) { | 
 |             callerName = ci.name; | 
 |             if (TextUtils.isEmpty(callerName)) { | 
 |                 // Do similar fallback as CallCard does. | 
 |                 // See also CallCard#updateDisplayForPerson(). | 
 |                 if (TextUtils.isEmpty(ci.phoneNumber)) { | 
 |                     callerName = PhoneUtils.getPresentationString(mInCallScreen, presentation); | 
 |                 } else if (!TextUtils.isEmpty(ci.cnapName)) { | 
 |                     // No name, but we do have a valid CNAP name, so use that. | 
 |                     callerName = ci.cnapName; | 
 |                 } else { | 
 |                     callerName = ci.phoneNumber; | 
 |                 } | 
 |             } else { | 
 |                 callerNumber = ci.phoneNumber; | 
 |                 callerNumberType = ci.phoneLabel; | 
 |             } | 
 |         } | 
 |  | 
 |         // 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); | 
 |         } | 
 |     } | 
 |  | 
 |     /** | 
 |      * Ends the specified connection on a conference call.  This method is | 
 |      * run (via a closure containing a row index and Connection) when the | 
 |      * user clicks the "End" button on a specific row in the Manage | 
 |      * conference UI. | 
 |      */ | 
 |     public void endConferenceConnection(int i, Connection connection) { | 
 |         if (DBG) log("===> ENDING conference connection " + i | 
 |                       + ": Connection " + connection); | 
 |         // The actual work of ending the connection: | 
 |         PhoneUtils.hangup(connection); | 
 |         // No need to manually update the "Manage conference" UI here; | 
 |         // that'll happen automatically very soon (when we get the | 
 |         // onDisconnect() callback triggered by this hangup() call.) | 
 |     } | 
 |  | 
 |     /** | 
 |      * Separates out the specified connection on a conference call.  This | 
 |      * method is run (via a closure containing a row index and Connection) | 
 |      * when the user clicks the "Separate" (i.e. "Private") button on a | 
 |      * specific row in the Manage conference UI. | 
 |      */ | 
 |     public void separateConferenceConnection(int i, Connection connection) { | 
 |         if (DBG) log("===> SEPARATING conference connection " + i | 
 |                       + ": Connection " + connection); | 
 |  | 
 |         PhoneUtils.separateCall(connection); | 
 |  | 
 |         // Note that separateCall() automagically makes the | 
 |         // newly-separated call into the foreground call (which is the | 
 |         // desired UI), so there's no need to do any further | 
 |         // call-switching here. | 
 |         // There's also no need to manually update (or hide) the "Manage | 
 |         // conference" UI; that'll happen on its own in a moment (when we | 
 |         // get the phone state change event triggered by the call to | 
 |         // separateCall().) | 
 |     } | 
 |  | 
 |  | 
 |     private void log(String msg) { | 
 |         Log.d(LOG_TAG, msg); | 
 |     } | 
 | } |