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