1 /* 2 * Copyright (C) 2013 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.incallui; 18 19 import com.android.incallui.AudioModeProvider.AudioModeListener; 20 import com.android.incallui.InCallPresenter.InCallState; 21 import com.android.incallui.InCallPresenter.InCallStateListener; 22 import com.android.incallui.InCallPresenter.IncomingCallListener; 23 import com.android.services.telephony.common.AudioMode; 24 import com.android.services.telephony.common.Call; 25 import com.android.services.telephony.common.Call.Capabilities; 26 27 import android.telephony.PhoneNumberUtils; 28 29 /** 30 * Logic for call buttons. 31 */ 32 public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButtonUi> 33 implements InCallStateListener, AudioModeListener, IncomingCallListener { 34 35 private Call mCall; 36 private boolean mAutomaticallyMuted = false; 37 private boolean mPreviousMuteState = false; 38 39 private boolean mShowGenericMerge = false; 40 private boolean mShowManageConference = false; 41 42 private InCallState mPreviousState = null; 43 44 public CallButtonPresenter() { 45 } 46 47 @Override 48 public void onUiReady(CallButtonUi ui) { 49 super.onUiReady(ui); 50 51 AudioModeProvider.getInstance().addListener(this); 52 53 // register for call state changes last 54 InCallPresenter.getInstance().addListener(this); 55 InCallPresenter.getInstance().addIncomingCallListener(this); 56 } 57 58 @Override 59 public void onUiUnready(CallButtonUi ui) { 60 super.onUiUnready(ui); 61 62 InCallPresenter.getInstance().removeListener(this); 63 AudioModeProvider.getInstance().removeListener(this); 64 InCallPresenter.getInstance().removeIncomingCallListener(this); 65 } 66 67 @Override 68 public void onStateChange(InCallState state, CallList callList) { 69 70 if (state == InCallState.OUTGOING) { 71 mCall = callList.getOutgoingCall(); 72 } else if (state == InCallState.INCALL) { 73 mCall = callList.getActiveOrBackgroundCall(); 74 75 // When connected to voice mail, automatically shows the dialpad. 76 // (On previous releases we showed it when in-call shows up, before waiting for 77 // OUTGOING. We may want to do that once we start showing "Voice mail" label on 78 // the dialpad too.) 79 if (mPreviousState == InCallState.OUTGOING 80 && mCall != null && PhoneNumberUtils.isVoiceMailNumber(mCall.getNumber())) { 81 getUi().displayDialpad(true); 82 } 83 } else if (state == InCallState.INCOMING) { 84 getUi().displayDialpad(false); 85 mCall = null; 86 } else { 87 mCall = null; 88 } 89 updateUi(state, mCall); 90 91 mPreviousState = state; 92 } 93 94 @Override 95 public void onIncomingCall(InCallState state, Call call) { 96 onStateChange(state, CallList.getInstance()); 97 } 98 99 @Override 100 public void onAudioMode(int mode) { 101 if (getUi() != null) { 102 getUi().setAudio(mode); 103 } 104 } 105 106 @Override 107 public void onSupportedAudioMode(int mask) { 108 if (getUi() != null) { 109 getUi().setSupportedAudio(mask); 110 } 111 } 112 113 @Override 114 public void onMute(boolean muted) { 115 if (getUi() != null) { 116 getUi().setMute(muted); 117 } 118 } 119 120 public int getAudioMode() { 121 return AudioModeProvider.getInstance().getAudioMode(); 122 } 123 124 public int getSupportedAudio() { 125 return AudioModeProvider.getInstance().getSupportedModes(); 126 } 127 128 public void setAudioMode(int mode) { 129 130 // TODO: Set a intermediate state in this presenter until we get 131 // an update for onAudioMode(). This will make UI response immediate 132 // if it turns out to be slow 133 134 Log.d(this, "Sending new Audio Mode: " + AudioMode.toString(mode)); 135 CallCommandClient.getInstance().setAudioMode(mode); 136 } 137 138 /** 139 * Function assumes that bluetooth is not supported. 140 */ 141 public void toggleSpeakerphone() { 142 // this function should not be called if bluetooth is available 143 if (0 != (AudioMode.BLUETOOTH & getSupportedAudio())) { 144 145 // It's clear the UI is wrong, so update the supported mode once again. 146 Log.e(this, "toggling speakerphone not allowed when bluetooth supported."); 147 getUi().setSupportedAudio(getSupportedAudio()); 148 return; 149 } 150 151 int newMode = AudioMode.SPEAKER; 152 153 // if speakerphone is already on, change to wired/earpiece 154 if (getAudioMode() == AudioMode.SPEAKER) { 155 newMode = AudioMode.WIRED_OR_EARPIECE; 156 } 157 158 setAudioMode(newMode); 159 } 160 161 public void endCallClicked() { 162 if (mCall == null) { 163 return; 164 } 165 166 CallCommandClient.getInstance().disconnectCall(mCall.getCallId()); 167 } 168 169 public void manageConferenceButtonClicked() { 170 getUi().displayManageConferencePanel(true); 171 } 172 173 public void muteClicked(boolean checked) { 174 Log.d(this, "turning on mute: " + checked); 175 176 CallCommandClient.getInstance().mute(checked); 177 } 178 179 public void holdClicked(boolean checked) { 180 if (mCall == null) { 181 return; 182 } 183 184 Log.d(this, "holding: " + mCall.getCallId()); 185 186 CallCommandClient.getInstance().hold(mCall.getCallId(), checked); 187 } 188 189 public void mergeClicked() { 190 CallCommandClient.getInstance().merge(); 191 } 192 193 public void addCallClicked() { 194 // Automatically mute the current call 195 mAutomaticallyMuted = true; 196 mPreviousMuteState = AudioModeProvider.getInstance().getMute(); 197 // Simulate a click on the mute button 198 muteClicked(true); 199 200 CallCommandClient.getInstance().addCall(); 201 } 202 203 public void swapClicked() { 204 CallCommandClient.getInstance().swap(); 205 } 206 207 public void showDialpadClicked(boolean checked) { 208 Log.v(this, "Show dialpad " + String.valueOf(checked)); 209 getUi().displayDialpad(checked); 210 updateExtraButtonRow(); 211 } 212 213 private void updateUi(InCallState state, Call call) { 214 final CallButtonUi ui = getUi(); 215 if (ui == null) { 216 return; 217 } 218 219 final boolean isEnabled = state.isConnectingOrConnected() && 220 !state.isIncoming() && call != null; 221 222 ui.setEnabled(isEnabled); 223 224 Log.d(this, "Updating call UI for call: ", call); 225 226 if (isEnabled) { 227 Log.v(this, "Show hold ", call.can(Capabilities.SUPPORT_HOLD)); 228 Log.v(this, "Enable hold", call.can(Capabilities.HOLD)); 229 Log.v(this, "Show merge ", call.can(Capabilities.MERGE_CALLS)); 230 Log.v(this, "Show swap ", call.can(Capabilities.SWAP_CALLS)); 231 Log.v(this, "Show add call ", call.can(Capabilities.ADD_CALL)); 232 Log.v(this, "Show mute ", call.can(Capabilities.MUTE)); 233 234 final boolean canMerge = call.can(Capabilities.MERGE_CALLS); 235 final boolean canAdd = call.can(Capabilities.ADD_CALL); 236 final boolean isGenericConference = call.can(Capabilities.GENERIC_CONFERENCE); 237 238 239 final boolean showMerge = !isGenericConference && canMerge; 240 241 if (showMerge) { 242 ui.showMerge(true); 243 ui.showAddCall(false); 244 } else { 245 ui.showMerge(false); 246 ui.showAddCall(true); 247 ui.enableAddCall(canAdd); 248 } 249 250 final boolean canHold = call.can(Capabilities.HOLD); 251 final boolean canSwap = call.can(Capabilities.SWAP_CALLS); 252 final boolean supportHold = call.can(Capabilities.SUPPORT_HOLD); 253 254 if (canHold) { 255 ui.showHold(true); 256 ui.setHold(call.getState() == Call.State.ONHOLD); 257 ui.enableHold(true); 258 ui.showSwap(false); 259 } else if (canSwap) { 260 ui.showHold(false); 261 ui.showSwap(true); 262 } else { 263 // Neither "Hold" nor "Swap" is available. This can happen for two 264 // reasons: 265 // (1) this is a transient state on a device that *can* 266 // normally hold or swap, or 267 // (2) this device just doesn't have the concept of hold/swap. 268 // 269 // In case (1), show the "Hold" button in a disabled state. In case 270 // (2), remove the button entirely. (This means that the button row 271 // will only have 4 buttons on some devices.) 272 273 if (supportHold) { 274 ui.showHold(true); 275 ui.enableHold(false); 276 ui.setHold(call.getState() == Call.State.ONHOLD); 277 ui.showSwap(false); 278 } else { 279 ui.showHold(false); 280 ui.showSwap(false); 281 } 282 } 283 284 ui.enableMute(call.can(Capabilities.MUTE)); 285 286 // Finally, update the "extra button row": It's displayed above the 287 // "End" button, but only if necessary. Also, it's never displayed 288 // while the dialpad is visible (since it would overlap.) 289 // 290 // The row contains two buttons: 291 // 292 // - "Manage conference" (used only on GSM devices) 293 // - "Merge" button (used only on CDMA devices) 294 295 mShowGenericMerge = isGenericConference && canMerge; 296 mShowManageConference = (call.isConferenceCall() && !isGenericConference); 297 298 updateExtraButtonRow(); 299 } 300 } 301 302 private void updateExtraButtonRow() { 303 final boolean showExtraButtonRow = (mShowGenericMerge || mShowManageConference) && 304 !getUi().isDialpadVisible(); 305 306 Log.d(this, "isGeneric: " + mShowGenericMerge); 307 Log.d(this, "mShowManageConference : " + mShowManageConference); 308 Log.d(this, "mShowGenericMerge: " + mShowGenericMerge); 309 if (showExtraButtonRow) { 310 if (mShowGenericMerge) { 311 getUi().showGenericMergeButton(); 312 } else if (mShowManageConference) { 313 getUi().showManageConferenceCallButton(); 314 } 315 } else { 316 getUi().hideExtraRow(); 317 } 318 } 319 320 public void refreshMuteState() { 321 // Restore the previous mute state 322 if (mAutomaticallyMuted && 323 AudioModeProvider.getInstance().getMute() != mPreviousMuteState) { 324 if (getUi() == null) { 325 return; 326 } 327 muteClicked(mPreviousMuteState); 328 } 329 mAutomaticallyMuted = false; 330 } 331 332 public interface CallButtonUi extends Ui { 333 void setEnabled(boolean on); 334 void setMute(boolean on); 335 void enableMute(boolean enabled); 336 void setHold(boolean on); 337 void showHold(boolean show); 338 void enableHold(boolean enabled); 339 void showMerge(boolean show); 340 void showSwap(boolean show); 341 void showAddCall(boolean show); 342 void enableAddCall(boolean enabled); 343 void displayDialpad(boolean on); 344 boolean isDialpadVisible(); 345 void setAudio(int mode); 346 void setSupportedAudio(int mask); 347 void showManageConferenceCallButton(); 348 void showGenericMergeButton(); 349 void hideExtraRow(); 350 void displayManageConferencePanel(boolean on); 351 } 352 } 353