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.telephony.PhoneNumberUtils; 20 import android.util.Log; 21 22 import com.android.internal.telephony.Call; 23 import com.android.internal.telephony.Connection; 24 import com.android.internal.telephony.Phone; 25 import com.android.internal.telephony.CallManager; 26 import com.android.internal.telephony.PhoneConstants; 27 import com.android.internal.telephony.TelephonyCapabilities; 28 29 /** 30 * Helper class to keep track of enabledness, visibility, and "on/off" 31 * or "checked" state of the various controls available in the in-call 32 * UI, based on the current telephony state. 33 * 34 * This class is independent of the exact UI controls used on any given 35 * device. To avoid cluttering up the "view" code (i.e. InCallTouchUi) 36 * with logic about which functions are available right now, we instead 37 * have that logic here, and provide simple boolean flags to indicate the 38 * state and/or enabledness of all possible in-call user operations. 39 * 40 * (In other words, this is the "model" that corresponds to the "view" 41 * implemented by InCallTouchUi.) 42 */ 43 public class InCallControlState { 44 private static final String LOG_TAG = "InCallControlState"; 45 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2); 46 47 private InCallScreen mInCallScreen; 48 private CallManager mCM; 49 50 // 51 // Our "public API": Boolean flags to indicate the state and/or 52 // enabledness of all possible in-call user operations: 53 // 54 55 public boolean manageConferenceVisible; 56 public boolean manageConferenceEnabled; 57 // 58 public boolean canAddCall; 59 // 60 public boolean canEndCall; 61 // 62 public boolean canSwap; 63 public boolean canMerge; 64 // 65 public boolean bluetoothEnabled; 66 public boolean bluetoothIndicatorOn; 67 // 68 public boolean speakerEnabled; 69 public boolean speakerOn; 70 // 71 public boolean canMute; 72 public boolean muteIndicatorOn; 73 // 74 public boolean dialpadEnabled; 75 public boolean dialpadVisible; 76 // 77 /** True if the "Hold" function is *ever* available on this device */ 78 public boolean supportsHold; 79 /** True if the call is currently on hold */ 80 public boolean onHold; 81 /** True if the "Hold" or "Unhold" function should be available right now */ 82 // TODO: this name is misleading. Let's break this apart into 83 // separate canHold and canUnhold flags, and have the caller look at 84 // "canHold || canUnhold" to decide whether the hold/unhold UI element 85 // should be visible. 86 public boolean canHold; 87 88 89 public InCallControlState(InCallScreen inCallScreen, CallManager cm) { 90 if (DBG) log("InCallControlState constructor..."); 91 mInCallScreen = inCallScreen; 92 mCM = cm; 93 } 94 95 /** 96 * Updates all our public boolean flags based on the current state of 97 * the Phone. 98 */ 99 public void update() { 100 final PhoneConstants.State state = mCM.getState(); // coarse-grained voice call state 101 final Call fgCall = mCM.getActiveFgCall(); 102 final Call.State fgCallState = fgCall.getState(); 103 final boolean hasActiveForegroundCall = (fgCallState == Call.State.ACTIVE); 104 final boolean hasHoldingCall = mCM.hasActiveBgCall(); 105 106 // Manage conference: 107 if (TelephonyCapabilities.supportsConferenceCallManagement(fgCall.getPhone())) { 108 // This item is visible only if the foreground call is a 109 // conference call, and it's enabled unless the "Manage 110 // conference" UI is already up. 111 manageConferenceVisible = PhoneUtils.isConferenceCall(fgCall); 112 manageConferenceEnabled = 113 manageConferenceVisible && !mInCallScreen.isManageConferenceMode(); 114 } else { 115 // This device has no concept of managing a conference call. 116 manageConferenceVisible = false; 117 manageConferenceEnabled = false; 118 } 119 120 // "Add call": 121 canAddCall = PhoneUtils.okToAddCall(mCM); 122 123 // "End call": always enabled unless the phone is totally idle. 124 // Note that while the phone is ringing, the InCallTouchUi widget isn't 125 // visible at all, so the state of the End button doesn't matter. However 126 // we *do* still set canEndCall to true in this case, purely to prevent a 127 // UI glitch when the InCallTouchUi widget first appears, immediately after 128 // answering an incoming call. 129 canEndCall = (mCM.hasActiveFgCall() || mCM.hasActiveRingingCall() || mCM.hasActiveBgCall()); 130 131 // Swap / merge calls 132 canSwap = PhoneUtils.okToSwapCalls(mCM); 133 canMerge = PhoneUtils.okToMergeCalls(mCM); 134 135 // "Bluetooth": 136 if (mInCallScreen.isBluetoothAvailable()) { 137 bluetoothEnabled = true; 138 bluetoothIndicatorOn = mInCallScreen.isBluetoothAudioConnectedOrPending(); 139 } else { 140 bluetoothEnabled = false; 141 bluetoothIndicatorOn = false; 142 } 143 144 // "Speaker": always enabled unless the phone is totally idle. 145 // The current speaker state comes from the AudioManager. 146 speakerEnabled = (state != PhoneConstants.State.IDLE); 147 speakerOn = PhoneUtils.isSpeakerOn(mInCallScreen); 148 149 // "Mute": only enabled when the foreground call is ACTIVE. 150 // (It's meaningless while on hold, or while DIALING/ALERTING.) 151 // It's also explicitly disabled during emergency calls or if 152 // emergency callback mode (ECM) is active. 153 Connection c = fgCall.getLatestConnection(); 154 boolean isEmergencyCall = false; 155 if (c != null) isEmergencyCall = 156 PhoneNumberUtils.isLocalEmergencyNumber(c.getAddress(), 157 fgCall.getPhone().getContext()); 158 boolean isECM = PhoneUtils.isPhoneInEcm(fgCall.getPhone()); 159 if (isEmergencyCall || isECM) { // disable "Mute" item 160 canMute = false; 161 muteIndicatorOn = false; 162 } else { 163 canMute = hasActiveForegroundCall; 164 muteIndicatorOn = PhoneUtils.getMute(); 165 } 166 167 // "Dialpad": Enabled only when it's OK to use the dialpad in the 168 // first place. 169 dialpadEnabled = mInCallScreen.okToShowDialpad(); 170 171 // Also keep track of whether the dialpad is currently "opened" 172 // (i.e. visible). 173 dialpadVisible = mInCallScreen.isDialerOpened(); 174 175 // "Hold: 176 if (TelephonyCapabilities.supportsHoldAndUnhold(fgCall.getPhone())) { 177 // This phone has the concept of explicit "Hold" and "Unhold" actions. 178 supportsHold = true; 179 // "On hold" means that there's a holding call and 180 // *no* foreground call. (If there *is* a foreground call, 181 // that's "two lines in use".) 182 onHold = hasHoldingCall && (fgCallState == Call.State.IDLE); 183 // The "Hold" control is disabled entirely if there's 184 // no way to either hold or unhold in the current state. 185 boolean okToHold = hasActiveForegroundCall && !hasHoldingCall; 186 boolean okToUnhold = onHold; 187 canHold = okToHold || okToUnhold; 188 } else if (hasHoldingCall && (fgCallState == Call.State.IDLE)) { 189 // Even when foreground phone device doesn't support hold/unhold, phone devices 190 // for background holding calls may do. 191 // 192 // If the foreground call is ACTIVE, we should turn on "swap" button instead. 193 final Call bgCall = mCM.getFirstActiveBgCall(); 194 if (bgCall != null && 195 TelephonyCapabilities.supportsHoldAndUnhold(bgCall.getPhone())) { 196 supportsHold = true; 197 onHold = true; 198 canHold = true; 199 } 200 } else { 201 // This device has no concept of "putting a call on hold." 202 supportsHold = false; 203 onHold = false; 204 canHold = false; 205 } 206 207 if (DBG) dumpState(); 208 } 209 210 public void dumpState() { 211 log("InCallControlState:"); 212 log(" manageConferenceVisible: " + manageConferenceVisible); 213 log(" manageConferenceEnabled: " + manageConferenceEnabled); 214 log(" canAddCall: " + canAddCall); 215 log(" canEndCall: " + canEndCall); 216 log(" canSwap: " + canSwap); 217 log(" canMerge: " + canMerge); 218 log(" bluetoothEnabled: " + bluetoothEnabled); 219 log(" bluetoothIndicatorOn: " + bluetoothIndicatorOn); 220 log(" speakerEnabled: " + speakerEnabled); 221 log(" speakerOn: " + speakerOn); 222 log(" canMute: " + canMute); 223 log(" muteIndicatorOn: " + muteIndicatorOn); 224 log(" dialpadEnabled: " + dialpadEnabled); 225 log(" dialpadVisible: " + dialpadVisible); 226 log(" onHold: " + onHold); 227 log(" canHold: " + canHold); 228 } 229 230 private void log(String msg) { 231 Log.d(LOG_TAG, msg); 232 } 233 } 234