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. (Some devices use onscreen touchable buttons, for example, and 35 * other devices use menu items.) To avoid cluttering up the InCallMenu 36 * and InCallTouchUi code with logic about which functions are available 37 * right now, we instead have that logic here, and provide simple boolean 38 * flags to indicate the state and/or enabledness of all possible in-call 39 * user operations. 40 * 41 * (In other words, this is the "model" that corresponds to the "view" 42 * implemented by InCallMenu and InCallTouchUi.) 43 */ 44 public class InCallControlState { 45 private static final String LOG_TAG = "InCallControlState"; 46 private static final boolean DBG = (PhoneApp.DBG_LEVEL >= 2); 47 48 private InCallScreen mInCallScreen; 49 private CallManager mCM; 50 51 // 52 // Our "public API": Boolean flags to indicate the state and/or 53 // enabledness of all possible in-call user operations: 54 // 55 56 public boolean manageConferenceVisible; 57 public boolean manageConferenceEnabled; 58 // 59 public boolean canAddCall; 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 Call fgCall = mCM.getActiveFgCall(); 100 final Call.State fgCallState = fgCall.getState(); 101 final boolean hasActiveForegroundCall = (fgCallState == Call.State.ACTIVE); 102 final boolean hasHoldingCall = mCM.hasActiveBgCall(); 103 104 // Manage conference: 105 if (TelephonyCapabilities.supportsConferenceCallManagement(fgCall.getPhone())) { 106 // This item is visible only if the foreground call is a 107 // conference call, and it's enabled unless the "Manage 108 // conference" UI is already up. 109 manageConferenceVisible = PhoneUtils.isConferenceCall(fgCall); 110 manageConferenceEnabled = 111 manageConferenceVisible && !mInCallScreen.isManageConferenceMode(); 112 } else { 113 // This device has no concept of managing a conference call. 114 manageConferenceVisible = false; 115 manageConferenceEnabled = false; 116 } 117 118 // "Add call": 119 canAddCall = PhoneUtils.okToAddCall(mCM); 120 121 // Swap / merge calls 122 canSwap = PhoneUtils.okToSwapCalls(mCM); 123 canMerge = PhoneUtils.okToMergeCalls(mCM); 124 125 // "Bluetooth": 126 if (mInCallScreen.isBluetoothAvailable()) { 127 bluetoothEnabled = true; 128 bluetoothIndicatorOn = mInCallScreen.isBluetoothAudioConnectedOrPending(); 129 } else { 130 bluetoothEnabled = false; 131 bluetoothIndicatorOn = false; 132 } 133 134 // "Speaker": always enabled. 135 // The current speaker state comes from the AudioManager. 136 speakerEnabled = true; 137 speakerOn = PhoneUtils.isSpeakerOn(mInCallScreen); 138 139 // "Mute": only enabled when the foreground call is ACTIVE. 140 // (It's meaningless while on hold, or while DIALING/ALERTING.) 141 // It's also explicitly disabled during emergency calls or if 142 // emergency callback mode (ECM) is active. 143 Connection c = fgCall.getLatestConnection(); 144 boolean isEmergencyCall = false; 145 if (c != null) isEmergencyCall = PhoneNumberUtils.isEmergencyNumber(c.getAddress()); 146 boolean isECM = PhoneUtils.isPhoneInEcm(fgCall.getPhone()); 147 if (isEmergencyCall || isECM) { // disable "Mute" item 148 canMute = false; 149 muteIndicatorOn = false; 150 } else { 151 canMute = hasActiveForegroundCall; 152 muteIndicatorOn = PhoneUtils.getMute(); 153 } 154 155 // "Dialpad": Enabled only when it's OK to use the dialpad in the 156 // first place. 157 dialpadEnabled = mInCallScreen.okToShowDialpad(); 158 159 // Also keep track of whether the dialpad is currently "opened" 160 // (i.e. visible). 161 dialpadVisible = mInCallScreen.isDialerOpened(); 162 163 // "Hold: 164 if (TelephonyCapabilities.supportsHoldAndUnhold(fgCall.getPhone())) { 165 // This phone has the concept of explicit "Hold" and "Unhold" actions. 166 supportsHold = true; 167 // "On hold" means that there's a holding call and 168 // *no* foreground call. (If there *is* a foreground call, 169 // that's "two lines in use".) 170 onHold = hasHoldingCall && (fgCallState == Call.State.IDLE); 171 // The "Hold" control is disabled entirely if there's 172 // no way to either hold or unhold in the current state. 173 boolean okToHold = hasActiveForegroundCall && !hasHoldingCall; 174 boolean okToUnhold = onHold; 175 canHold = okToHold || okToUnhold; 176 } else { 177 // This device has no concept of "putting a call on hold." 178 supportsHold = false; 179 onHold = false; 180 canHold = false; 181 } 182 183 if (DBG) dumpState(); 184 } 185 186 public void dumpState() { 187 log("InCallControlState:"); 188 log(" manageConferenceVisible: " + manageConferenceVisible); 189 log(" manageConferenceEnabled: " + manageConferenceEnabled); 190 log(" canAddCall: " + canAddCall); 191 log(" canSwap: " + canSwap); 192 log(" canMerge: " + canMerge); 193 log(" bluetoothEnabled: " + bluetoothEnabled); 194 log(" bluetoothIndicatorOn: " + bluetoothIndicatorOn); 195 log(" speakerEnabled: " + speakerEnabled); 196 log(" speakerOn: " + speakerOn); 197 log(" canMute: " + canMute); 198 log(" muteIndicatorOn: " + muteIndicatorOn); 199 log(" dialpadEnabled: " + dialpadEnabled); 200 log(" dialpadVisible: " + dialpadVisible); 201 log(" onHold: " + onHold); 202 log(" canHold: " + canHold); 203 } 204 205 private void log(String msg) { 206 Log.d(LOG_TAG, msg); 207 } 208 } 209