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