CallCardPresenter.java revision 1df52df7a0248814fbd4575103059a8b427f5d9a
1/* 2 * Copyright (C) 2013 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 17package com.android.incallui; 18 19import android.graphics.drawable.Drawable; 20import android.text.TextUtils; 21import android.text.format.DateUtils; 22 23import com.android.incallui.AudioModeProvider.AudioModeListener; 24import com.android.incallui.ContactInfoCache.ContactCacheEntry; 25import com.android.incallui.ContactInfoCache.ContactInfoCacheCallback; 26import com.android.incallui.InCallPresenter.InCallState; 27import com.android.incallui.InCallPresenter.InCallStateListener; 28 29import com.android.services.telephony.common.AudioMode; 30import com.android.services.telephony.common.Call; 31import com.android.services.telephony.common.Call.DisconnectCause; 32 33/** 34 * Presenter for the Call Card Fragment. 35 * This class listens for changes to InCallState and passes it along to the fragment. 36 */ 37public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> 38 implements InCallStateListener, AudioModeListener, ContactInfoCacheCallback { 39 40 private static final long CALL_TIME_UPDATE_INTERVAL = 1000; // in milliseconds 41 42 private AudioModeProvider mAudioModeProvider; 43 private ContactInfoCache mContactInfoCache; 44 private Call mPrimary; 45 private Call mSecondary; 46 private ContactCacheEntry mPrimaryContactInfo; 47 private ContactCacheEntry mSecondaryContactInfo; 48 private CallTimer mCallTimer; 49 50 public CallCardPresenter() { 51 52 // create the call timer 53 mCallTimer = new CallTimer(new Runnable() { 54 @Override 55 public void run() { 56 updateCallTime(); 57 } 58 }); 59 } 60 61 @Override 62 public void onUiReady(CallCardUi ui) { 63 super.onUiReady(ui); 64 65 if (mAudioModeProvider != null) { 66 mAudioModeProvider.addListener(this); 67 } 68 } 69 70 @Override 71 public void onUiUnready(CallCardUi ui) { 72 super.onUiUnready(ui); 73 74 if (mAudioModeProvider != null) { 75 mAudioModeProvider.removeListener(this); 76 } 77 mPrimary = null; 78 mPrimaryContactInfo = null; 79 mSecondaryContactInfo = null; 80 } 81 82 @Override 83 public void onStateChange(InCallState state, CallList callList) { 84 final CallCardUi ui = getUi(); 85 if (ui == null) { 86 return; 87 } 88 89 Call primary = null; 90 Call secondary = null; 91 92 if (state == InCallState.INCOMING) { 93 primary = callList.getIncomingCall(); 94 } else if (state == InCallState.OUTGOING) { 95 primary = callList.getOutgoingCall(); 96 97 // getCallToDisplay doesn't go through outgoing or incoming calls. It will return the 98 // highest priority call to display as the secondary call. 99 secondary = getCallToDisplay(callList, null, true); 100 } else if (state == InCallState.INCALL) { 101 primary = getCallToDisplay(callList, null, false); 102 secondary = getCallToDisplay(callList, primary, true); 103 } 104 105 Logger.d(this, "Primary call: " + primary); 106 Logger.d(this, "Secondary call: " + secondary); 107 108 mPrimary = primary; 109 mSecondary = secondary; 110 111 // Query for contact data. This will call back on onContactInfoComplete at least once 112 // synchronously, and potentially a second time asynchronously if it needs to make 113 // a full query for the data. 114 // It is in that callback that we set the values into the Ui. 115 startContactInfoSearch(); 116 117 // Start/Stop the call time update timer 118 if (mPrimary != null && mPrimary.getState() == Call.State.ACTIVE) { 119 Logger.d(this, "Starting the calltime timer"); 120 mCallTimer.start(CALL_TIME_UPDATE_INTERVAL); 121 } else { 122 Logger.d(this, "Canceling the calltime timer"); 123 mCallTimer.cancel(); 124 ui.setPrimaryCallElapsedTime(false, null); 125 } 126 127 // Set the call state 128 if (mPrimary != null) { 129 final boolean bluetoothOn = mAudioModeProvider != null && 130 mAudioModeProvider.getAudioMode() == AudioMode.BLUETOOTH; 131 ui.setCallState(mPrimary.getState(), mPrimary.getDisconnectCause(), bluetoothOn); 132 } else { 133 ui.setCallState(Call.State.IDLE, Call.DisconnectCause.UNKNOWN, false); 134 } 135 } 136 137 @Override 138 public void onAudioMode(int mode) { 139 if (mPrimary != null && getUi() != null) { 140 final boolean bluetoothOn = (AudioMode.BLUETOOTH == mode); 141 142 getUi().setCallState(mPrimary.getState(), mPrimary.getDisconnectCause(), bluetoothOn); 143 } 144 } 145 146 @Override 147 public void onSupportedAudioMode(int mask) { 148 } 149 150 public void updateCallTime() { 151 final CallCardUi ui = getUi(); 152 153 if (ui == null || mPrimary == null || mPrimary.getState() != Call.State.ACTIVE) { 154 if (ui != null) { 155 ui.setPrimaryCallElapsedTime(false, null); 156 } 157 mCallTimer.cancel(); 158 } else { 159 final long callStart = mPrimary.getConnectTime(); 160 final long duration = System.currentTimeMillis() - callStart; 161 ui.setPrimaryCallElapsedTime(true, DateUtils.formatElapsedTime(duration / 1000)); 162 } 163 } 164 165 166 public void setContactInfoCache(ContactInfoCache cache) { 167 mContactInfoCache = cache; 168 startContactInfoSearch(); 169 } 170 171 /** 172 * Starts a query for more contact data for the save primary and secondary calls. 173 */ 174 private void startContactInfoSearch() { 175 if (mPrimary != null && mContactInfoCache != null) { 176 mContactInfoCache.findInfo(mPrimary, this); 177 } else { 178 mPrimaryContactInfo = null; 179 updatePrimaryDisplayInfo(); 180 } 181 182 if (mSecondary != null && mContactInfoCache != null) { 183 mContactInfoCache.findInfo(mSecondary, this); 184 } else { 185 mSecondaryContactInfo = null; 186 updateSecondaryDisplayInfo(); 187 } 188 } 189 190 /** 191 * Get the highest priority call to display. 192 * Goes through the calls and chooses which to return based on priority of which type of call 193 * to display to the user. Callers can use the "ignore" feature to get the second best call 194 * by passing a previously found primary call as ignore. 195 * 196 * @param ignore A call to ignore if found. 197 */ 198 private Call getCallToDisplay(CallList callList, Call ignore, boolean skipDisconnected) { 199 200 // Active calls come second. An active call always gets precedent. 201 Call retval = callList.getActiveCall(); 202 if (retval != null && retval != ignore) { 203 return retval; 204 } 205 206 // Disconnected calls get primary position if there are no active calls 207 // to let user know quickly what call has disconnected. Disconnected 208 // calls are very short lived. 209 if (!skipDisconnected) { 210 retval = callList.getDisconnectedCall(); 211 if (retval != null && retval != ignore) { 212 return retval; 213 } 214 } 215 216 // Then we go to background call (calls on hold) 217 retval = callList.getBackgroundCall(); 218 if (retval != null && retval != ignore) { 219 return retval; 220 } 221 222 // Lastly, we go to a second background call. 223 retval = callList.getSecondBackgroundCall(); 224 225 return retval; 226 } 227 228 /** 229 * Callback received when Contact info data query completes. 230 */ 231 @Override 232 public void onContactInfoComplete(int callId, ContactCacheEntry entry) { 233 if (mPrimary != null && mPrimary.getCallId() == callId) { 234 mPrimaryContactInfo = entry; 235 updatePrimaryDisplayInfo(); 236 } 237 if (mSecondary != null && mSecondary.getCallId() == callId) { 238 mSecondaryContactInfo = entry; 239 updateSecondaryDisplayInfo(); 240 } 241 242 } 243 244 private void updatePrimaryDisplayInfo() { 245 final CallCardUi ui = getUi(); 246 if (ui == null) { 247 return; 248 } 249 250 if (mPrimaryContactInfo != null) { 251 final String name = getNameForCall(mPrimaryContactInfo); 252 final String number = getNumberForCall(mPrimaryContactInfo); 253 final boolean nameIsNumber = name != null && name.equals(mPrimaryContactInfo.number); 254 ui.setPrimary(number, name, nameIsNumber, mPrimaryContactInfo.label, 255 mPrimaryContactInfo.photo, mPrimary.isConferenceCall()); 256 } else { 257 // reset to nothing (like at end of call) 258 ui.setPrimary(null, null, false, null, null, false); 259 } 260 261 } 262 263 /** 264 * Gets the name to display for the call. 265 */ 266 private static String getNameForCall(ContactCacheEntry contactInfo) { 267 if (TextUtils.isEmpty(contactInfo.name)) { 268 return contactInfo.number; 269 } 270 return contactInfo.name; 271 } 272 273 /** 274 * Gets the number to display for a call. 275 */ 276 private static String getNumberForCall(ContactCacheEntry contactInfo) { 277 // If the name is empty, we use the number for the name...so dont show a second 278 // number in the number field 279 if (TextUtils.isEmpty(contactInfo.name)) { 280 return null; 281 } 282 return contactInfo.number; 283 } 284 285 private void updateSecondaryDisplayInfo() { 286 final CallCardUi ui = getUi(); 287 if (ui == null) { 288 return; 289 } 290 291 if (mSecondaryContactInfo != null) { 292 final String name = getNameForCall(mSecondaryContactInfo); 293 ui.setSecondary(true, getNameForCall(mSecondaryContactInfo), 294 mSecondaryContactInfo.label, mSecondaryContactInfo.photo); 295 } else { 296 // reset to nothing so that it starts off blank next time we use it. 297 ui.setSecondary(false, null, null, null); 298 } 299 } 300 301 public void setAudioModeProvider(AudioModeProvider audioModeProvider) { 302 mAudioModeProvider = audioModeProvider; 303 mAudioModeProvider.addListener(this); 304 } 305 306 public interface CallCardUi extends Ui { 307 void setVisible(boolean on); 308 void setPrimary(String number, String name, boolean nameIsNumber, String label, 309 Drawable photo, boolean isConference); 310 void setSecondary(boolean show, String name, String label, Drawable photo); 311 void setCallState(int state, Call.DisconnectCause cause, boolean bluetoothOn); 312 void setPrimaryCallElapsedTime(boolean show, String duration); 313 } 314} 315