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 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 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 */ 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. 123 // We keep this button enabled even in states where it's technically not 124 // needed, like during the brief "Call ended" state, where the phone is 125 // IDLE. (See InCallScreen.internalHangup() for the handling of that 126 // case.) 127 canEndCall = true; 128 129 // Swap / merge calls 130 canSwap = PhoneUtils.okToSwapCalls(mCM); 131 canMerge = PhoneUtils.okToMergeCalls(mCM); 132 133 // "Bluetooth": 134 if (mInCallScreen.isBluetoothAvailable()) { 135 bluetoothEnabled = true; 136 bluetoothIndicatorOn = mInCallScreen.isBluetoothAudioConnectedOrPending(); 137 } else { 138 bluetoothEnabled = false; 139 bluetoothIndicatorOn = false; 140 } 141 142 // "Speaker": always enabled unless the phone is totally idle. 143 // The current speaker state comes from the AudioManager. 144 speakerEnabled = (state != Phone.State.IDLE); 145 speakerOn = PhoneUtils.isSpeakerOn(mInCallScreen); 146 147 // "Mute": only enabled when the foreground call is ACTIVE. 148 // (It's meaningless while on hold, or while DIALING/ALERTING.) 149 // It's also explicitly disabled during emergency calls or if 150 // emergency callback mode (ECM) is active. 151 Connection c = fgCall.getLatestConnection(); 152 boolean isEmergencyCall = false; 153 if (c != null) isEmergencyCall = 154 PhoneNumberUtils.isLocalEmergencyNumber(c.getAddress(), 155 fgCall.getPhone().getContext()); 156 boolean isECM = PhoneUtils.isPhoneInEcm(fgCall.getPhone()); 157 if (isEmergencyCall || isECM) { // disable "Mute" item 158 canMute = false; 159 muteIndicatorOn = false; 160 } else { 161 canMute = hasActiveForegroundCall; 162 muteIndicatorOn = PhoneUtils.getMute(); 163 } 164 165 // "Dialpad": Enabled only when it's OK to use the dialpad in the 166 // first place. 167 dialpadEnabled = mInCallScreen.okToShowDialpad(); 168 169 // Also keep track of whether the dialpad is currently "opened" 170 // (i.e. visible). 171 dialpadVisible = mInCallScreen.isDialerOpened(); 172 173 // "Hold: 174 if (TelephonyCapabilities.supportsHoldAndUnhold(fgCall.getPhone())) { 175 // This phone has the concept of explicit "Hold" and "Unhold" actions. 176 supportsHold = true; 177 // "On hold" means that there's a holding call and 178 // *no* foreground call. (If there *is* a foreground call, 179 // that's "two lines in use".) 180 onHold = hasHoldingCall && (fgCallState == Call.State.IDLE); 181 // The "Hold" control is disabled entirely if there's 182 // no way to either hold or unhold in the current state. 183 boolean okToHold = hasActiveForegroundCall && !hasHoldingCall; 184 boolean okToUnhold = onHold; 185 canHold = okToHold || okToUnhold; 186 } else if (hasHoldingCall && (fgCallState == Call.State.IDLE)) { 187 // Even when foreground phone device doesn't support hold/unhold, phone devices 188 // for background holding calls may do. 189 // 190 // If the foreground call is ACTIVE, we should turn on "swap" button instead. 191 final Call bgCall = mCM.getFirstActiveBgCall(); 192 if (bgCall != null && 193 TelephonyCapabilities.supportsHoldAndUnhold(bgCall.getPhone())) { 194 supportsHold = true; 195 onHold = true; 196 canHold = true; 197 } 198 } else { 199 // This device has no concept of "putting a call on hold." 200 supportsHold = false; 201 onHold = false; 202 canHold = false; 203 } 204 205 if (DBG) dumpState(); 206 } 207 208 public void dumpState() { 209 log("InCallControlState:"); 210 log(" manageConferenceVisible: " + manageConferenceVisible); 211 log(" manageConferenceEnabled: " + manageConferenceEnabled); 212 log(" canAddCall: " + canAddCall); 213 log(" canEndCall: " + canEndCall); 214 log(" canSwap: " + canSwap); 215 log(" canMerge: " + canMerge); 216 log(" bluetoothEnabled: " + bluetoothEnabled); 217 log(" bluetoothIndicatorOn: " + bluetoothIndicatorOn); 218 log(" speakerEnabled: " + speakerEnabled); 219 log(" speakerOn: " + speakerOn); 220 log(" canMute: " + canMute); 221 log(" muteIndicatorOn: " + muteIndicatorOn); 222 log(" dialpadEnabled: " + dialpadEnabled); 223 log(" dialpadVisible: " + dialpadVisible); 224 log(" onHold: " + onHold); 225 log(" canHold: " + canHold); 226 } 227 228 private void log(String msg) { 229 Log.d(LOG_TAG, msg); 230 } 231 } 232