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 26 27 /** 28 * Helper class to keep track of enabledness, visibility, and "on/off" 29 * or "checked" state of the various controls available in the in-call 30 * UI, based on the current telephony state. 31 * 32 * This class is independent of the exact UI controls used on any given 33 * device. (Some devices use onscreen touchable buttons, for example, and 34 * other devices use menu items.) To avoid cluttering up the InCallMenu 35 * and InCallTouchUi code with logic about which functions are available 36 * right now, we instead have that logic here, and provide simple boolean 37 * flags to indicate the state and/or enabledness of all possible in-call 38 * user operations. 39 * 40 * (In other words, this is the "model" that corresponds to the "view" 41 * implemented by InCallMenu and InCallTouchUi.) 42 */ 43 public class InCallControlState { 44 private static final String LOG_TAG = "InCallControlState"; 45 private static final boolean DBG = (PhoneApp.DBG_LEVEL >= 2); 46 47 private InCallScreen mInCallScreen; 48 private Phone mPhone; 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 canSwap; 61 public boolean canMerge; 62 // 63 public boolean bluetoothEnabled; 64 public boolean bluetoothIndicatorOn; 65 // 66 public boolean speakerEnabled; 67 public boolean speakerOn; 68 // 69 public boolean canMute; 70 public boolean muteIndicatorOn; 71 // 72 public boolean dialpadEnabled; 73 public boolean dialpadVisible; 74 // 75 /** True if the "Hold" function is *ever* available on this device */ 76 public boolean supportsHold; 77 /** True if the call is currently on hold */ 78 public boolean onHold; 79 /** True if the "Hold" or "Unhold" function should be available right now */ 80 // TODO: this name is misleading. Let's break this apart into 81 // separate canHold and canUnhold flags, and have the caller look at 82 // "canHold || canUnhold" to decide whether the hold/unhold UI element 83 // should be visible. 84 public boolean canHold; 85 86 87 public InCallControlState(InCallScreen inCallScreen, Phone phone) { 88 if (DBG) log("InCallControlState constructor..."); 89 mInCallScreen = inCallScreen; 90 mPhone = phone; 91 } 92 93 /** 94 * Updates all our public boolean flags based on the current state of 95 * the Phone. 96 */ 97 public void update() { 98 final boolean hasRingingCall = !mPhone.getRingingCall().isIdle(); 99 final Call fgCall = mPhone.getForegroundCall(); 100 final Call.State fgCallState = fgCall.getState(); 101 final boolean hasActiveForegroundCall = (fgCallState == Call.State.ACTIVE); 102 final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle(); 103 104 // Manage conference: 105 int phoneType = mPhone.getPhoneType(); 106 if (phoneType == Phone.PHONE_TYPE_GSM) { 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 if (phoneType == Phone.PHONE_TYPE_CDMA) { 114 // CDMA has no concept of managing a conference call. 115 manageConferenceVisible = false; 116 manageConferenceEnabled = false; 117 } else { 118 throw new IllegalStateException("Unexpected phone type: " + phoneType); 119 } 120 121 // "Add call": 122 canAddCall = PhoneUtils.okToAddCall(mPhone); 123 124 // Swap / merge calls 125 canSwap = PhoneUtils.okToSwapCalls(mPhone); 126 canMerge = PhoneUtils.okToMergeCalls(mPhone); 127 128 // "Bluetooth": 129 if (mInCallScreen.isBluetoothAvailable()) { 130 bluetoothEnabled = true; 131 bluetoothIndicatorOn = mInCallScreen.isBluetoothAudioConnectedOrPending(); 132 } else { 133 bluetoothEnabled = false; 134 bluetoothIndicatorOn = false; 135 } 136 137 // "Speaker": always enabled. 138 // The current speaker state comes from the AudioManager. 139 speakerEnabled = true; 140 speakerOn = PhoneUtils.isSpeakerOn(mInCallScreen); 141 142 // "Mute": only enabled when the foreground call is ACTIVE. 143 // (It's meaningless while on hold, or while DIALING/ALERTING.) 144 // Also disabled (on CDMA devices) during emergency calls. 145 if (phoneType == Phone.PHONE_TYPE_CDMA) { 146 Connection c = fgCall.getLatestConnection(); 147 boolean isEmergencyCall = false; 148 if (c != null) isEmergencyCall = PhoneNumberUtils.isEmergencyNumber(c.getAddress()); 149 150 if (isEmergencyCall) { // disable "Mute" item 151 canMute = false; 152 muteIndicatorOn = false; 153 } else { 154 canMute = hasActiveForegroundCall; 155 muteIndicatorOn = PhoneUtils.getMute(mPhone); 156 } 157 } else if (phoneType == Phone.PHONE_TYPE_GSM) { 158 canMute = hasActiveForegroundCall; 159 muteIndicatorOn = PhoneUtils.getMute(mPhone); 160 } 161 162 // "Dialpad": Enabled only when it's OK to use the dialpad in the 163 // first place. 164 dialpadEnabled = mInCallScreen.okToShowDialpad(); 165 166 // Also keep track of whether the dialpad is currently "opened" 167 // (i.e. visible). 168 dialpadVisible = mInCallScreen.isDialerOpened(); 169 170 // "Hold: 171 if (phoneType == Phone.PHONE_TYPE_GSM) { 172 // GSM phones have the concept of "Hold" and "Unhold". 173 supportsHold = true; 174 // "On hold" means that there's a holding call and 175 // *no* foreground call. (If there *is* a foreground call, 176 // that's "two lines in use".) 177 onHold = hasHoldingCall && (fgCallState == Call.State.IDLE); 178 // The "Hold" control is disabled entirely if there's 179 // no way to either hold or unhold in the current state. 180 boolean okToHold = hasActiveForegroundCall && !hasHoldingCall; 181 boolean okToUnhold = onHold; 182 canHold = okToHold || okToUnhold; 183 } else if (phoneType == Phone.PHONE_TYPE_CDMA) { 184 // CDMA has no concept of "putting a call on hold." 185 supportsHold = false; 186 onHold = false; 187 canHold = false; 188 } 189 190 if (DBG) dumpState(); 191 } 192 193 public void dumpState() { 194 log("InCallControlState:"); 195 log(" manageConferenceVisible: " + manageConferenceVisible); 196 log(" manageConferenceEnabled: " + manageConferenceEnabled); 197 log(" canAddCall: " + canAddCall); 198 log(" canSwap: " + canSwap); 199 log(" canMerge: " + canMerge); 200 log(" bluetoothEnabled: " + bluetoothEnabled); 201 log(" bluetoothIndicatorOn: " + bluetoothIndicatorOn); 202 log(" speakerEnabled: " + speakerEnabled); 203 log(" speakerOn: " + speakerOn); 204 log(" canMute: " + canMute); 205 log(" muteIndicatorOn: " + muteIndicatorOn); 206 log(" dialpadEnabled: " + dialpadEnabled); 207 log(" dialpadVisible: " + dialpadVisible); 208 log(" onHold: " + onHold); 209 log(" canHold: " + canHold); 210 } 211 212 private void log(String msg) { 213 Log.d(LOG_TAG, msg); 214 } 215 } 216