1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.phone; 18 19 import android.os.SystemProperties; 20 import android.text.TextUtils; 21 import android.util.Log; 22 import android.view.View; 23 import android.view.ViewGroup; 24 import android.view.ViewStub; 25 import android.widget.Button; 26 import android.widget.Chronometer; 27 import android.widget.ImageButton; 28 import android.widget.TextView; 29 30 import com.android.internal.telephony.CallerInfo; 31 import com.android.internal.telephony.CallerInfoAsyncQuery; 32 import com.android.internal.telephony.CallManager; 33 import com.android.internal.telephony.Connection; 34 35 import java.util.List; 36 37 38 /** 39 * Helper class to initialize and run the InCallScreen's "Manage conference" UI. 40 */ 41 public class ManageConferenceUtils { 42 private static final String LOG_TAG = "ManageConferenceUtils"; 43 private static final boolean DBG = 44 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1); 45 46 /** 47 * CallerInfoAsyncQuery.OnQueryCompleteListener implementation. 48 * 49 * This object listens for results from the caller-id info queries we 50 * fire off in updateManageConferenceRow(), and updates the 51 * corresponding conference row. 52 */ 53 private final class QueryCompleteListener 54 implements CallerInfoAsyncQuery.OnQueryCompleteListener { 55 private final int mConferencCallListIndex; 56 57 public QueryCompleteListener(int index) { 58 mConferencCallListIndex = index; 59 } 60 61 @Override 62 public void onQueryComplete(int token, Object cookie, CallerInfo ci) { 63 if (DBG) log("callerinfo query complete, updating UI." + ci); 64 65 Connection connection = (Connection) cookie; 66 int presentation = connection.getNumberPresentation(); 67 68 // get the viewgroup (conference call list item) and make it visible 69 ViewGroup viewGroup = mConferenceCallList[mConferencCallListIndex]; 70 viewGroup.setVisibility(View.VISIBLE); 71 72 // update the list item with this information. 73 displayCallerInfoForConferenceRow(ci, presentation, 74 (TextView) viewGroup.findViewById(R.id.conferenceCallerName), 75 (TextView) viewGroup.findViewById(R.id.conferenceCallerNumberType), 76 (TextView) viewGroup.findViewById(R.id.conferenceCallerNumber)); 77 } 78 } 79 80 private InCallScreen mInCallScreen; 81 private CallManager mCM; 82 83 // "Manage conference" UI elements and state 84 private ViewGroup mManageConferencePanel; 85 private View mButtonManageConferenceDone; 86 private ViewGroup[] mConferenceCallList; 87 private int mNumCallersInConference; 88 private Chronometer mConferenceTime; 89 90 // See CallTracker.MAX_CONNECTIONS_PER_CALL 91 private static final int MAX_CALLERS_IN_CONFERENCE = 5; 92 93 public ManageConferenceUtils(InCallScreen inCallScreen, CallManager cm) { 94 if (DBG) log("ManageConferenceUtils constructor..."); 95 mInCallScreen = inCallScreen; 96 mCM = cm; 97 } 98 99 public void initManageConferencePanel() { 100 if (DBG) log("initManageConferencePanel()..."); 101 if (mManageConferencePanel == null) { 102 if (DBG) log("initManageConferencePanel: first-time initialization!"); 103 104 // Inflate the ViewStub, look up and initialize the UI elements. 105 ViewStub stub = (ViewStub) mInCallScreen.findViewById(R.id.manageConferencePanelStub); 106 stub.inflate(); 107 108 mManageConferencePanel = 109 (ViewGroup) mInCallScreen.findViewById(R.id.manageConferencePanel); 110 if (mManageConferencePanel == null) { 111 throw new IllegalStateException("Couldn't find manageConferencePanel!"); 112 } 113 114 // set up the Conference Call chronometer 115 mConferenceTime = 116 (Chronometer) mInCallScreen.findViewById(R.id.manageConferencePanelHeader); 117 mConferenceTime.setFormat(mInCallScreen.getString(R.string.caller_manage_header)); 118 119 // Create list of conference call widgets 120 mConferenceCallList = new ViewGroup[MAX_CALLERS_IN_CONFERENCE]; 121 122 final int[] viewGroupIdList = { R.id.caller0, R.id.caller1, R.id.caller2, 123 R.id.caller3, R.id.caller4 }; 124 for (int i = 0; i < MAX_CALLERS_IN_CONFERENCE; i++) { 125 mConferenceCallList[i] = 126 (ViewGroup) mInCallScreen.findViewById(viewGroupIdList[i]); 127 } 128 129 mButtonManageConferenceDone = mInCallScreen.findViewById(R.id.manage_done); 130 mButtonManageConferenceDone.setOnClickListener(mInCallScreen); 131 } 132 } 133 134 /** 135 * Shows or hides the manageConferencePanel. 136 */ 137 public void setPanelVisible(boolean visible) { 138 if (mManageConferencePanel != null) { 139 mManageConferencePanel.setVisibility(visible ? View.VISIBLE : View.GONE); 140 } 141 } 142 143 /** 144 * Starts the "conference time" chronometer. 145 */ 146 public void startConferenceTime(long base) { 147 if (mConferenceTime != null) { 148 mConferenceTime.setBase(base); 149 mConferenceTime.start(); 150 } 151 } 152 153 /** 154 * Stops the "conference time" chronometer. 155 */ 156 public void stopConferenceTime() { 157 if (mConferenceTime != null) { 158 mConferenceTime.stop(); 159 } 160 } 161 162 public int getNumCallersInConference() { 163 return mNumCallersInConference; 164 } 165 166 /** 167 * Updates the "Manage conference" UI based on the specified List of 168 * connections. 169 * 170 * @param connections the List of connections belonging to 171 * the current foreground call; size must be greater than 1 172 * (or it wouldn't be a conference call in the first place.) 173 */ 174 public void updateManageConferencePanel(List<Connection> connections) { 175 mNumCallersInConference = connections.size(); 176 if (DBG) log("updateManageConferencePanel()... num connections in conference = " 177 + mNumCallersInConference); 178 179 // Can we give the user the option to separate out ("go private with") a single 180 // caller from this conference? 181 final boolean hasActiveCall = mCM.hasActiveFgCall(); 182 final boolean hasHoldingCall = mCM.hasActiveBgCall(); 183 boolean canSeparate = !(hasActiveCall && hasHoldingCall); 184 185 for (int i = 0; i < MAX_CALLERS_IN_CONFERENCE; i++) { 186 if (i < mNumCallersInConference) { 187 // Fill in the row in the UI for this caller. 188 Connection connection = (Connection) connections.get(i); 189 updateManageConferenceRow(i, connection, canSeparate); 190 } else { 191 // Blank out this row in the UI 192 updateManageConferenceRow(i, null, false); 193 } 194 } 195 } 196 197 /** 198 * Updates a single row of the "Manage conference" UI. (One row in this 199 * UI represents a single caller in the conference.) 200 * 201 * @param i the row to update 202 * @param connection the Connection corresponding to this caller. 203 * If null, that means this is an "empty slot" in the conference, 204 * so hide this row in the UI. 205 * @param canSeparate if true, show a "Separate" (i.e. "Private") button 206 * on this row in the UI. 207 */ 208 public void updateManageConferenceRow(final int i, 209 final Connection connection, 210 boolean canSeparate) { 211 if (DBG) log("updateManageConferenceRow(" + i + ")... connection = " + connection); 212 213 if (connection != null) { 214 // Activate this row of the Manage conference panel: 215 mConferenceCallList[i].setVisibility(View.VISIBLE); 216 217 // get the relevant children views 218 View endButton = mConferenceCallList[i].findViewById(R.id.conferenceCallerDisconnect); 219 View separateButton = mConferenceCallList[i].findViewById( 220 R.id.conferenceCallerSeparate); 221 TextView nameTextView = (TextView) mConferenceCallList[i].findViewById( 222 R.id.conferenceCallerName); 223 TextView numberTextView = (TextView) mConferenceCallList[i].findViewById( 224 R.id.conferenceCallerNumber); 225 TextView numberTypeTextView = (TextView) mConferenceCallList[i].findViewById( 226 R.id.conferenceCallerNumberType); 227 228 if (DBG) log("- button: " + endButton + ", nameTextView: " + nameTextView); 229 230 // Hook up this row's buttons. 231 View.OnClickListener endThisConnection = new View.OnClickListener() { 232 @Override 233 public void onClick(View v) { 234 endConferenceConnection(i, connection); 235 PhoneGlobals.getInstance().pokeUserActivity(); 236 } 237 }; 238 endButton.setOnClickListener(endThisConnection); 239 // 240 if (canSeparate) { 241 View.OnClickListener separateThisConnection = new View.OnClickListener() { 242 @Override 243 public void onClick(View v) { 244 separateConferenceConnection(i, connection); 245 PhoneGlobals.getInstance().pokeUserActivity(); 246 } 247 }; 248 separateButton.setOnClickListener(separateThisConnection); 249 separateButton.setVisibility(View.VISIBLE); 250 } else { 251 separateButton.setVisibility(View.INVISIBLE); 252 } 253 254 // Name/number for this caller. 255 QueryCompleteListener listener = new QueryCompleteListener(i); 256 PhoneUtils.CallerInfoToken info = 257 PhoneUtils.startGetCallerInfo(mInCallScreen, 258 connection, listener, connection); 259 if (DBG) log(" - got info from startGetCallerInfo(): " + info); 260 261 // display the CallerInfo. 262 displayCallerInfoForConferenceRow(info.currentInfo, connection.getNumberPresentation(), 263 nameTextView, numberTypeTextView, numberTextView); 264 } else { 265 // Disable this row of the Manage conference panel: 266 mConferenceCallList[i].setVisibility(View.GONE); 267 } 268 } 269 270 /** 271 * Helper function to fill out the Conference Call(er) information 272 * for each item in the "Manage Conference Call" list. 273 * 274 * @param presentation presentation specified by {@link Connection}. 275 */ 276 public final void displayCallerInfoForConferenceRow(CallerInfo ci, int presentation, 277 TextView nameTextView, TextView numberTypeTextView, TextView numberTextView) { 278 // gather the correct name and number information. 279 String callerName = ""; 280 String callerNumber = ""; 281 String callerNumberType = ""; 282 if (ci != null) { 283 callerName = ci.name; 284 if (TextUtils.isEmpty(callerName)) { 285 // Do similar fallback as CallCard does. 286 // See also CallCard#updateDisplayForPerson(). 287 if (TextUtils.isEmpty(ci.phoneNumber)) { 288 callerName = PhoneUtils.getPresentationString(mInCallScreen, presentation); 289 } else if (!TextUtils.isEmpty(ci.cnapName)) { 290 // No name, but we do have a valid CNAP name, so use that. 291 callerName = ci.cnapName; 292 } else { 293 callerName = ci.phoneNumber; 294 } 295 } else { 296 callerNumber = ci.phoneNumber; 297 callerNumberType = ci.phoneLabel; 298 } 299 } 300 301 // set the caller name 302 nameTextView.setText(callerName); 303 304 // set the caller number in subscript, or make the field disappear. 305 if (TextUtils.isEmpty(callerNumber)) { 306 numberTextView.setVisibility(View.GONE); 307 numberTypeTextView.setVisibility(View.GONE); 308 } else { 309 numberTextView.setVisibility(View.VISIBLE); 310 numberTextView.setText(callerNumber); 311 numberTypeTextView.setVisibility(View.VISIBLE); 312 numberTypeTextView.setText(callerNumberType); 313 } 314 } 315 316 /** 317 * Ends the specified connection on a conference call. This method is 318 * run (via a closure containing a row index and Connection) when the 319 * user clicks the "End" button on a specific row in the Manage 320 * conference UI. 321 */ 322 public void endConferenceConnection(int i, Connection connection) { 323 if (DBG) log("===> ENDING conference connection " + i 324 + ": Connection " + connection); 325 // The actual work of ending the connection: 326 PhoneUtils.hangup(connection); 327 // No need to manually update the "Manage conference" UI here; 328 // that'll happen automatically very soon (when we get the 329 // onDisconnect() callback triggered by this hangup() call.) 330 } 331 332 /** 333 * Separates out the specified connection on a conference call. This 334 * method is run (via a closure containing a row index and Connection) 335 * when the user clicks the "Separate" (i.e. "Private") button on a 336 * specific row in the Manage conference UI. 337 */ 338 public void separateConferenceConnection(int i, Connection connection) { 339 if (DBG) log("===> SEPARATING conference connection " + i 340 + ": Connection " + connection); 341 342 PhoneUtils.separateCall(connection); 343 344 // Note that separateCall() automagically makes the 345 // newly-separated call into the foreground call (which is the 346 // desired UI), so there's no need to do any further 347 // call-switching here. 348 // There's also no need to manually update (or hide) the "Manage 349 // conference" UI; that'll happen on its own in a moment (when we 350 // get the phone state change event triggered by the call to 351 // separateCall().) 352 } 353 354 355 private void log(String msg) { 356 Log.d(LOG_TAG, msg); 357 } 358 } 359