1 /* 2 * Copyright (C) 2014 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.incallui; 18 19 import android.content.Context; 20 import android.database.Cursor; 21 import android.graphics.Point; 22 import android.net.Uri; 23 import android.os.AsyncTask; 24 import android.os.Handler; 25 import android.os.Looper; 26 import android.provider.ContactsContract; 27 import android.telecom.CallAudioState; 28 import android.telecom.Connection; 29 import android.telecom.InCallService.VideoCall; 30 import android.telecom.VideoProfile; 31 import android.telecom.VideoProfile.CameraCapabilities; 32 import android.view.Surface; 33 import android.widget.ImageView; 34 35 import com.android.contacts.common.ContactPhotoManager; 36 import com.android.incallui.InCallPresenter.InCallDetailsListener; 37 import com.android.incallui.InCallPresenter.InCallOrientationListener; 38 import com.android.incallui.InCallPresenter.InCallStateListener; 39 import com.android.incallui.InCallPresenter.IncomingCallListener; 40 import com.android.incallui.InCallVideoCallCallbackNotifier.SurfaceChangeListener; 41 import com.android.incallui.InCallVideoCallCallbackNotifier.VideoEventListener; 42 43 import java.util.Objects; 44 45 /** 46 * Logic related to the {@link VideoCallFragment} and for managing changes to the video calling 47 * surfaces based on other user interface events and incoming events from the 48 * {@class VideoCallListener}. 49 * <p> 50 * When a call's video state changes to bi-directional video, the 51 * {@link com.android.incallui.VideoCallPresenter} performs the following negotiation with the 52 * telephony layer: 53 * <ul> 54 * <li>{@code VideoCallPresenter} creates and informs telephony of the display surface.</li> 55 * <li>{@code VideoCallPresenter} creates the preview surface.</li> 56 * <li>{@code VideoCallPresenter} informs telephony of the currently selected camera.</li> 57 * <li>Telephony layer sends {@link CameraCapabilities}, including the 58 * dimensions of the video for the current camera.</li> 59 * <li>{@code VideoCallPresenter} adjusts size of the preview surface to match the aspect 60 * ratio of the camera.</li> 61 * <li>{@code VideoCallPresenter} informs telephony of the new preview surface.</li> 62 * </ul> 63 * <p> 64 * When downgrading to an audio-only video state, the {@code VideoCallPresenter} nulls both 65 * surfaces. 66 */ 67 public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi> implements 68 IncomingCallListener, InCallOrientationListener, InCallStateListener, 69 InCallDetailsListener, SurfaceChangeListener, VideoEventListener, 70 InCallVideoCallCallbackNotifier.SessionModificationListener, 71 InCallPresenter.InCallEventListener { 72 public static final String TAG = "VideoCallPresenter"; 73 74 public static final boolean DEBUG = false; 75 76 /** 77 * Runnable which is posted to schedule automatically entering fullscreen mode. 78 */ 79 private Runnable mAutoFullscreenRunnable = new Runnable() { 80 @Override 81 public void run() { 82 if (mAutoFullScreenPending) { 83 Log.v(this, "Automatically entering fullscreen mode."); 84 InCallPresenter.getInstance().setFullScreen(true); 85 mAutoFullScreenPending = false; 86 } else { 87 Log.v(this, "Skipping scheduled fullscreen mode."); 88 } 89 } 90 }; 91 92 /** 93 * Defines the state of the preview surface negotiation with the telephony layer. 94 */ 95 private class PreviewSurfaceState { 96 /** 97 * The camera has not yet been set on the {@link VideoCall}; negotiation has not yet 98 * started. 99 */ 100 private static final int NONE = 0; 101 102 /** 103 * The camera has been set on the {@link VideoCall}, but camera capabilities have not yet 104 * been received. 105 */ 106 private static final int CAMERA_SET = 1; 107 108 /** 109 * The camera capabilties have been received from telephony, but the surface has not yet 110 * been set on the {@link VideoCall}. 111 */ 112 private static final int CAPABILITIES_RECEIVED = 2; 113 114 /** 115 * The surface has been set on the {@link VideoCall}. 116 */ 117 private static final int SURFACE_SET = 3; 118 } 119 120 /** 121 * The minimum width or height of the preview surface. Used when re-sizing the preview surface 122 * to match the aspect ratio of the currently selected camera. 123 */ 124 private float mMinimumVideoDimension; 125 126 /** 127 * The current context. 128 */ 129 private Context mContext; 130 131 /** 132 * The call the video surfaces are currently related to 133 */ 134 private Call mPrimaryCall; 135 136 /** 137 * The {@link VideoCall} used to inform the video telephony layer of changes to the video 138 * surfaces. 139 */ 140 private VideoCall mVideoCall; 141 142 /** 143 * Determines if the current UI state represents a video call. 144 */ 145 private int mCurrentVideoState; 146 147 /** 148 * Call's current state 149 */ 150 private int mCurrentCallState = Call.State.INVALID; 151 152 /** 153 * Determines the device orientation (portrait/lanscape). 154 */ 155 private int mDeviceOrientation; 156 157 /** 158 * Tracks the state of the preview surface negotiation with the telephony layer. 159 */ 160 private int mPreviewSurfaceState = PreviewSurfaceState.NONE; 161 162 /** 163 * Saves the audio mode which was selected prior to going into a video call. 164 */ 165 private static int sPrevVideoAudioMode = AudioModeProvider.AUDIO_MODE_INVALID; 166 167 private static boolean mIsVideoMode = false; 168 169 /** 170 * Contact photo manager to retrieve cached contact photo information. 171 */ 172 private ContactPhotoManager mContactPhotoManager = null; 173 174 /** 175 * The URI for the user's profile photo, or {@code null} if not specified. 176 */ 177 private ContactInfoCache.ContactCacheEntry mProfileInfo = null; 178 179 /** 180 * UI thread handler used for delayed task execution. 181 */ 182 private Handler mHandler; 183 184 /** 185 * Determines whether video calls should automatically enter full screen mode after 186 * {@link #mAutoFullscreenTimeoutMillis} milliseconds. 187 */ 188 private boolean mIsAutoFullscreenEnabled = false; 189 190 /** 191 * Determines the number of milliseconds after which a video call will automatically enter 192 * fullscreen mode. Requires {@link #mIsAutoFullscreenEnabled} to be {@code true}. 193 */ 194 private int mAutoFullscreenTimeoutMillis = 0; 195 196 /** 197 * Determines if the countdown is currently running to automatically enter full screen video 198 * mode. 199 */ 200 private boolean mAutoFullScreenPending = false; 201 202 /** 203 * Initializes the presenter. 204 * 205 * @param context The current context. 206 */ init(Context context)207 public void init(Context context) { 208 mContext = context; 209 mMinimumVideoDimension = mContext.getResources().getDimension( 210 R.dimen.video_preview_small_dimension); 211 mHandler = new Handler(Looper.getMainLooper()); 212 mIsAutoFullscreenEnabled = mContext.getResources() 213 .getBoolean(R.bool.video_call_auto_fullscreen); 214 mAutoFullscreenTimeoutMillis = mContext.getResources().getInteger( 215 R.integer.video_call_auto_fullscreen_timeout); 216 } 217 218 /** 219 * Called when the user interface is ready to be used. 220 * 221 * @param ui The Ui implementation that is now ready to be used. 222 */ 223 @Override onUiReady(VideoCallUi ui)224 public void onUiReady(VideoCallUi ui) { 225 super.onUiReady(ui); 226 Log.d(this, "onUiReady:"); 227 228 // Register for call state changes last 229 InCallPresenter.getInstance().addListener(this); 230 InCallPresenter.getInstance().addDetailsListener(this); 231 InCallPresenter.getInstance().addIncomingCallListener(this); 232 InCallPresenter.getInstance().addOrientationListener(this); 233 // To get updates of video call details changes 234 InCallPresenter.getInstance().addDetailsListener(this); 235 236 // Register for surface and video events from {@link InCallVideoCallListener}s. 237 InCallVideoCallCallbackNotifier.getInstance().addSurfaceChangeListener(this); 238 InCallVideoCallCallbackNotifier.getInstance().addVideoEventListener(this); 239 InCallVideoCallCallbackNotifier.getInstance().addSessionModificationListener(this); 240 mCurrentVideoState = VideoProfile.STATE_AUDIO_ONLY; 241 mCurrentCallState = Call.State.INVALID; 242 } 243 244 /** 245 * Called when the user interface is no longer ready to be used. 246 * 247 * @param ui The Ui implementation that is no longer ready to be used. 248 */ 249 @Override onUiUnready(VideoCallUi ui)250 public void onUiUnready(VideoCallUi ui) { 251 super.onUiUnready(ui); 252 Log.d(this, "onUiUnready:"); 253 254 InCallPresenter.getInstance().removeListener(this); 255 InCallPresenter.getInstance().removeDetailsListener(this); 256 InCallPresenter.getInstance().removeIncomingCallListener(this); 257 InCallPresenter.getInstance().removeOrientationListener(this); 258 259 InCallVideoCallCallbackNotifier.getInstance().removeSurfaceChangeListener(this); 260 InCallVideoCallCallbackNotifier.getInstance().removeVideoEventListener(this); 261 InCallVideoCallCallbackNotifier.getInstance().removeSessionModificationListener(this); 262 } 263 264 /** 265 * Handles the creation of a surface in the {@link VideoCallFragment}. 266 * 267 * @param surface The surface which was created. 268 */ onSurfaceCreated(int surface)269 public void onSurfaceCreated(int surface) { 270 Log.d(this, "onSurfaceCreated surface=" + surface + " mVideoCall=" + mVideoCall); 271 Log.d(this, "onSurfaceCreated PreviewSurfaceState=" + mPreviewSurfaceState); 272 Log.d(this, "onSurfaceCreated presenter=" + this); 273 274 final VideoCallUi ui = getUi(); 275 if (ui == null || mVideoCall == null) { 276 Log.w(this, "onSurfaceCreated: Error bad state VideoCallUi=" + ui + " mVideoCall=" 277 + mVideoCall); 278 return; 279 } 280 281 // If the preview surface has just been created and we have already received camera 282 // capabilities, but not yet set the surface, we will set the surface now. 283 if (surface == VideoCallFragment.SURFACE_PREVIEW ) { 284 if (mPreviewSurfaceState == PreviewSurfaceState.CAPABILITIES_RECEIVED) { 285 mPreviewSurfaceState = PreviewSurfaceState.SURFACE_SET; 286 mVideoCall.setPreviewSurface(ui.getPreviewVideoSurface()); 287 } else if (mPreviewSurfaceState == PreviewSurfaceState.NONE && isCameraRequired()){ 288 enableCamera(mVideoCall, true); 289 } 290 } else if (surface == VideoCallFragment.SURFACE_DISPLAY) { 291 mVideoCall.setDisplaySurface(ui.getDisplayVideoSurface()); 292 } 293 } 294 295 /** 296 * Handles structural changes (format or size) to a surface. 297 * 298 * @param surface The surface which changed. 299 * @param format The new PixelFormat of the surface. 300 * @param width The new width of the surface. 301 * @param height The new height of the surface. 302 */ onSurfaceChanged(int surface, int format, int width, int height)303 public void onSurfaceChanged(int surface, int format, int width, int height) { 304 //Do stuff 305 } 306 307 /** 308 * Handles the destruction of a surface in the {@link VideoCallFragment}. 309 * Note: The surface is being released, that is, it is no longer valid. 310 * 311 * @param surface The surface which was destroyed. 312 */ onSurfaceReleased(int surface)313 public void onSurfaceReleased(int surface) { 314 Log.d(this, "onSurfaceReleased: mSurfaceId=" + surface); 315 if ( mVideoCall == null) { 316 Log.w(this, "onSurfaceReleased: VideoCall is null. mSurfaceId=" + 317 surface); 318 return; 319 } 320 321 if (surface == VideoCallFragment.SURFACE_DISPLAY) { 322 mVideoCall.setDisplaySurface(null); 323 } else if (surface == VideoCallFragment.SURFACE_PREVIEW) { 324 mVideoCall.setPreviewSurface(null); 325 enableCamera(mVideoCall, false); 326 } 327 } 328 329 /** 330 * Called by {@link VideoCallFragment} when the surface is detached from UI (TextureView). 331 * Note: The surface will be cached by {@link VideoCallFragment}, so we don't immediately 332 * null out incoming video surface. 333 * @see VideoCallPresenter#onSurfaceReleased(int) 334 * 335 * @param surface The surface which was detached. 336 */ onSurfaceDestroyed(int surface)337 public void onSurfaceDestroyed(int surface) { 338 Log.d(this, "onSurfaceDestroyed: mSurfaceId=" + surface); 339 if (mVideoCall == null) { 340 return; 341 } 342 343 final boolean isChangingConfigurations = 344 InCallPresenter.getInstance().isChangingConfigurations(); 345 Log.d(this, "onSurfaceDestroyed: isChangingConfigurations=" + isChangingConfigurations); 346 347 if (surface == VideoCallFragment.SURFACE_PREVIEW) { 348 if (!isChangingConfigurations) { 349 enableCamera(mVideoCall, false); 350 } else { 351 Log.w(this, "onSurfaceDestroyed: Activity is being destroyed due " 352 + "to configuration changes. Not closing the camera."); 353 } 354 } 355 } 356 357 /** 358 * Handles clicks on the video surfaces by toggling full screen state. 359 * Informs the {@link InCallPresenter} of the change so that it can inform the 360 * {@link CallCardPresenter} of the change. 361 * 362 * @param surfaceId The video surface receiving the click. 363 */ onSurfaceClick(int surfaceId)364 public void onSurfaceClick(int surfaceId) { 365 boolean isFullscreen = InCallPresenter.getInstance().toggleFullscreenMode(); 366 Log.v(this, "toggleFullScreen = " + isFullscreen); 367 } 368 369 /** 370 * Handles incoming calls. 371 * 372 * @param oldState The old in call state. 373 * @param newState The new in call state. 374 * @param call The call. 375 */ 376 @Override onIncomingCall(InCallPresenter.InCallState oldState, InCallPresenter.InCallState newState, Call call)377 public void onIncomingCall(InCallPresenter.InCallState oldState, 378 InCallPresenter.InCallState newState, Call call) { 379 // same logic should happen as with onStateChange() 380 onStateChange(oldState, newState, CallList.getInstance()); 381 } 382 383 /** 384 * Handles state changes (including incoming calls) 385 * 386 * @param newState The in call state. 387 * @param callList The call list. 388 */ 389 @Override onStateChange(InCallPresenter.InCallState oldState, InCallPresenter.InCallState newState, CallList callList)390 public void onStateChange(InCallPresenter.InCallState oldState, 391 InCallPresenter.InCallState newState, CallList callList) { 392 Log.d(this, "onStateChange oldState" + oldState + " newState=" + newState + 393 " isVideoMode=" + isVideoMode()); 394 395 if (newState == InCallPresenter.InCallState.NO_CALLS) { 396 updateAudioMode(false); 397 398 if (isVideoMode()) { 399 exitVideoMode(); 400 } 401 402 cleanupSurfaces(); 403 } 404 405 // Determine the primary active call). 406 Call primary = null; 407 408 // Determine the call which is the focus of the user's attention. In the case of an 409 // incoming call waiting call, the primary call is still the active video call, however 410 // the determination of whether we should be in fullscreen mode is based on the type of the 411 // incoming call, not the active video call. 412 Call currentCall = null; 413 414 if (newState == InCallPresenter.InCallState.INCOMING) { 415 // We don't want to replace active video call (primary call) 416 // with a waiting call, since user may choose to ignore/decline the waiting call and 417 // this should have no impact on current active video call, that is, we should not 418 // change the camera or UI unless the waiting VT call becomes active. 419 primary = callList.getActiveCall(); 420 currentCall = callList.getIncomingCall(); 421 if (!CallUtils.isActiveVideoCall(primary)) { 422 primary = callList.getIncomingCall(); 423 } 424 } else if (newState == InCallPresenter.InCallState.OUTGOING) { 425 currentCall = primary = callList.getOutgoingCall(); 426 } else if (newState == InCallPresenter.InCallState.PENDING_OUTGOING) { 427 currentCall = primary = callList.getPendingOutgoingCall(); 428 } else if (newState == InCallPresenter.InCallState.INCALL) { 429 currentCall = primary = callList.getActiveCall(); 430 } 431 432 final boolean primaryChanged = !Objects.equals(mPrimaryCall, primary); 433 Log.d(this, "onStateChange primaryChanged=" + primaryChanged); 434 Log.d(this, "onStateChange primary= " + primary); 435 Log.d(this, "onStateChange mPrimaryCall = " + mPrimaryCall); 436 if (primaryChanged) { 437 onPrimaryCallChanged(primary); 438 } else if (mPrimaryCall != null) { 439 updateVideoCall(primary); 440 } 441 updateCallCache(primary); 442 443 // If the call context changed, potentially exit fullscreen or schedule auto enter of 444 // fullscreen mode. 445 // If the current call context is no longer a video call, exit fullscreen mode. 446 maybeExitFullscreen(currentCall); 447 // Schedule auto-enter of fullscreen mode if the current call context is a video call 448 maybeAutoEnterFullscreen(currentCall); 449 } 450 451 /** 452 * Handles a change to the fullscreen mode of the app. 453 * 454 * @param isFullscreenMode {@code true} if the app is now fullscreen, {@code false} otherwise. 455 */ 456 @Override onFullscreenModeChanged(boolean isFullscreenMode)457 public void onFullscreenModeChanged(boolean isFullscreenMode) { 458 cancelAutoFullScreen(); 459 } 460 checkForVideoStateChange(Call call)461 private void checkForVideoStateChange(Call call) { 462 final boolean isVideoCall = CallUtils.isVideoCall(call); 463 final boolean hasVideoStateChanged = mCurrentVideoState != call.getVideoState(); 464 465 Log.d(this, "checkForVideoStateChange: isVideoCall= " + isVideoCall 466 + " hasVideoStateChanged=" + hasVideoStateChanged + " isVideoMode=" 467 + isVideoMode() + " previousVideoState: " + 468 VideoProfile.videoStateToString(mCurrentVideoState) + " newVideoState: " 469 + VideoProfile.videoStateToString(call.getVideoState())); 470 471 if (!hasVideoStateChanged) { 472 return; 473 } 474 475 updateCameraSelection(call); 476 477 if (isVideoCall) { 478 enterVideoMode(call); 479 } else if (isVideoMode()) { 480 exitVideoMode(); 481 } 482 } 483 checkForCallStateChange(Call call)484 private void checkForCallStateChange(Call call) { 485 final boolean isVideoCall = CallUtils.isVideoCall(call); 486 final boolean hasCallStateChanged = mCurrentCallState != call.getState(); 487 488 Log.d(this, "checkForCallStateChange: isVideoCall= " + isVideoCall 489 + " hasCallStateChanged=" + 490 hasCallStateChanged + " isVideoMode=" + isVideoMode()); 491 492 if (!hasCallStateChanged) { 493 return; 494 } 495 496 if (isVideoCall) { 497 final InCallCameraManager cameraManager = InCallPresenter.getInstance(). 498 getInCallCameraManager(); 499 500 String prevCameraId = cameraManager.getActiveCameraId(); 501 updateCameraSelection(call); 502 String newCameraId = cameraManager.getActiveCameraId(); 503 504 if (!Objects.equals(prevCameraId, newCameraId) && CallUtils.isActiveVideoCall(call)) { 505 enableCamera(call.getVideoCall(), true); 506 } 507 } 508 509 // Make sure we hide or show the video UI if needed. 510 showVideoUi(call.getVideoState(), call.getState()); 511 } 512 cleanupSurfaces()513 private void cleanupSurfaces() { 514 final VideoCallUi ui = getUi(); 515 if (ui == null) { 516 Log.w(this, "cleanupSurfaces"); 517 return; 518 } 519 ui.cleanupSurfaces(); 520 } 521 onPrimaryCallChanged(Call newPrimaryCall)522 private void onPrimaryCallChanged(Call newPrimaryCall) { 523 final boolean isVideoCall = CallUtils.isVideoCall(newPrimaryCall); 524 final boolean isVideoMode = isVideoMode(); 525 526 Log.d(this, "onPrimaryCallChanged: isVideoCall=" + isVideoCall + " isVideoMode=" 527 + isVideoMode); 528 529 if (!isVideoCall && isVideoMode) { 530 // Terminate video mode if new primary call is not a video call 531 // and we are currently in video mode. 532 Log.d(this, "onPrimaryCallChanged: Exiting video mode..."); 533 exitVideoMode(); 534 } else if (isVideoCall) { 535 Log.d(this, "onPrimaryCallChanged: Entering video mode..."); 536 537 updateCameraSelection(newPrimaryCall); 538 enterVideoMode(newPrimaryCall); 539 } 540 } 541 isVideoMode()542 private boolean isVideoMode() { 543 return mIsVideoMode; 544 } 545 updateCallCache(Call call)546 private void updateCallCache(Call call) { 547 if (call == null) { 548 mCurrentVideoState = VideoProfile.STATE_AUDIO_ONLY; 549 mCurrentCallState = Call.State.INVALID; 550 mVideoCall = null; 551 mPrimaryCall = null; 552 } else { 553 mCurrentVideoState = call.getVideoState(); 554 mVideoCall = call.getVideoCall(); 555 mCurrentCallState = call.getState(); 556 mPrimaryCall = call; 557 } 558 } 559 560 /** 561 * Handles changes to the details of the call. The {@link VideoCallPresenter} is interested in 562 * changes to the video state. 563 * 564 * @param call The call for which the details changed. 565 * @param details The new call details. 566 */ 567 @Override onDetailsChanged(Call call, android.telecom.Call.Details details)568 public void onDetailsChanged(Call call, android.telecom.Call.Details details) { 569 Log.d(this, " onDetailsChanged call=" + call + " details=" + details + " mPrimaryCall=" 570 + mPrimaryCall); 571 if (call == null) { 572 return; 573 } 574 // If the details change is not for the currently active call no update is required. 575 if (!call.equals(mPrimaryCall)) { 576 Log.d(this," onDetailsChanged: Details not for current active call so returning. "); 577 return; 578 } 579 580 updateVideoCall(call); 581 582 updateCallCache(call); 583 } 584 updateVideoCall(Call call)585 private void updateVideoCall(Call call) { 586 checkForVideoCallChange(call); 587 checkForVideoStateChange(call); 588 checkForCallStateChange(call); 589 checkForOrientationAllowedChange(call); 590 } 591 checkForOrientationAllowedChange(Call call)592 private void checkForOrientationAllowedChange(Call call) { 593 InCallPresenter.getInstance().setInCallAllowsOrientationChange(CallUtils.isVideoCall(call)); 594 } 595 596 /** 597 * Checks for a change to the video call and changes it if required. 598 */ checkForVideoCallChange(Call call)599 private void checkForVideoCallChange(Call call) { 600 final VideoCall videoCall = call.getTelecommCall().getVideoCall(); 601 Log.d(this, "checkForVideoCallChange: videoCall=" + videoCall + " mVideoCall=" 602 + mVideoCall); 603 if (!Objects.equals(videoCall, mVideoCall)) { 604 changeVideoCall(call); 605 } 606 } 607 608 /** 609 * Handles a change to the video call. Sets the surfaces on the previous call to null and sets 610 * the surfaces on the new video call accordingly. 611 * 612 * @param videoCall The new video call. 613 */ changeVideoCall(Call call)614 private void changeVideoCall(Call call) { 615 final VideoCall videoCall = call.getTelecommCall().getVideoCall(); 616 Log.d(this, "changeVideoCall to videoCall=" + videoCall + " mVideoCall=" + mVideoCall); 617 // Null out the surfaces on the previous video call. 618 if (mVideoCall != null) { 619 // Log.d(this, "Null out the surfaces on the previous video call."); 620 // mVideoCall.setDisplaySurface(null); 621 // mVideoCall.setPreviewSurface(null); 622 } 623 624 final boolean hasChanged = mVideoCall == null && videoCall != null; 625 626 mVideoCall = videoCall; 627 if (mVideoCall == null || call == null) { 628 Log.d(this, "Video call or primary call is null. Return"); 629 return; 630 } 631 632 if (CallUtils.isVideoCall(call) && hasChanged) { 633 enterVideoMode(call); 634 } 635 } 636 isCameraRequired(int videoState)637 private static boolean isCameraRequired(int videoState) { 638 return VideoProfile.isBidirectional(videoState) || 639 VideoProfile.isTransmissionEnabled(videoState); 640 } 641 isCameraRequired()642 private boolean isCameraRequired() { 643 return mPrimaryCall != null ? isCameraRequired(mPrimaryCall.getVideoState()) : false; 644 } 645 646 /** 647 * Enters video mode by showing the video surfaces and making other adjustments (eg. audio). 648 * TODO(vt): Need to adjust size and orientation of preview surface here. 649 */ enterVideoMode(Call call)650 private void enterVideoMode(Call call) { 651 VideoCall videoCall = call.getVideoCall(); 652 int newVideoState = call.getVideoState(); 653 654 Log.d(this, "enterVideoMode videoCall= " + videoCall + " videoState: " + newVideoState); 655 VideoCallUi ui = getUi(); 656 if (ui == null) { 657 Log.e(this, "Error VideoCallUi is null so returning"); 658 return; 659 } 660 661 showVideoUi(newVideoState, call.getState()); 662 663 // Communicate the current camera to telephony and make a request for the camera 664 // capabilities. 665 if (videoCall != null) { 666 if (ui.isDisplayVideoSurfaceCreated()) { 667 Log.d(this, "Calling setDisplaySurface with " + ui.getDisplayVideoSurface()); 668 videoCall.setDisplaySurface(ui.getDisplayVideoSurface()); 669 } 670 671 final int rotation = ui.getCurrentRotation(); 672 if (rotation != VideoCallFragment.ORIENTATION_UNKNOWN) { 673 videoCall.setDeviceOrientation(InCallPresenter.toRotationAngle(rotation)); 674 } 675 676 enableCamera(videoCall, isCameraRequired(newVideoState)); 677 } 678 mCurrentVideoState = newVideoState; 679 updateAudioMode(true); 680 681 mIsVideoMode = true; 682 683 maybeAutoEnterFullscreen(call); 684 } 685 686 //TODO: Move this into Telecom. InCallUI should not be this close to audio functionality. updateAudioMode(boolean enableSpeaker)687 private void updateAudioMode(boolean enableSpeaker) { 688 if (!isSpeakerEnabledForVideoCalls()) { 689 Log.d(this, "Speaker is disabled. Can't update audio mode"); 690 return; 691 } 692 693 final TelecomAdapter telecomAdapter = TelecomAdapter.getInstance(); 694 final boolean isPrevAudioModeValid = 695 sPrevVideoAudioMode != AudioModeProvider.AUDIO_MODE_INVALID; 696 697 Log.d(this, "Is previous audio mode valid = " + isPrevAudioModeValid + " enableSpeaker is " 698 + enableSpeaker); 699 700 // Set audio mode to previous mode if enableSpeaker is false. 701 if (isPrevAudioModeValid && !enableSpeaker) { 702 telecomAdapter.setAudioRoute(sPrevVideoAudioMode); 703 sPrevVideoAudioMode = AudioModeProvider.AUDIO_MODE_INVALID; 704 return; 705 } 706 707 int currentAudioMode = AudioModeProvider.getInstance().getAudioMode(); 708 709 // Set audio mode to speaker if enableSpeaker is true and bluetooth or headset are not 710 // connected and it's a video call. 711 if (!isAudioRouteEnabled(currentAudioMode, 712 CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_WIRED_HEADSET) && 713 !isPrevAudioModeValid && enableSpeaker && CallUtils.isVideoCall(mPrimaryCall)) { 714 sPrevVideoAudioMode = currentAudioMode; 715 716 Log.d(this, "Routing audio to speaker"); 717 telecomAdapter.setAudioRoute(CallAudioState.ROUTE_SPEAKER); 718 } 719 } 720 isSpeakerEnabledForVideoCalls()721 private static boolean isSpeakerEnabledForVideoCalls() { 722 // TODO: Make this a carrier configurable setting. For now this is always true. b/20090407 723 return true; 724 } 725 enableCamera(VideoCall videoCall, boolean isCameraRequired)726 private void enableCamera(VideoCall videoCall, boolean isCameraRequired) { 727 Log.d(this, "enableCamera: VideoCall=" + videoCall + " enabling=" + isCameraRequired); 728 if (videoCall == null) { 729 Log.w(this, "enableCamera: VideoCall is null."); 730 return; 731 } 732 733 if (isCameraRequired) { 734 InCallCameraManager cameraManager = InCallPresenter.getInstance(). 735 getInCallCameraManager(); 736 videoCall.setCamera(cameraManager.getActiveCameraId()); 737 mPreviewSurfaceState = PreviewSurfaceState.CAMERA_SET; 738 739 videoCall.requestCameraCapabilities(); 740 } else { 741 mPreviewSurfaceState = PreviewSurfaceState.NONE; 742 videoCall.setCamera(null); 743 } 744 } 745 746 /** 747 * Exits video mode by hiding the video surfaces and making other adjustments (eg. audio). 748 */ exitVideoMode()749 private void exitVideoMode() { 750 Log.d(this, "exitVideoMode"); 751 752 showVideoUi(VideoProfile.STATE_AUDIO_ONLY, Call.State.ACTIVE); 753 enableCamera(mVideoCall, false); 754 InCallPresenter.getInstance().setFullScreen(false); 755 756 mIsVideoMode = false; 757 } 758 759 /** 760 * Based on the current video state and call state, show or hide the incoming and 761 * outgoing video surfaces. The outgoing video surface is shown any time video is transmitting. 762 * The incoming video surface is shown whenever the video is un-paused and active. 763 * 764 * @param videoState The video state. 765 * @param callState The call state. 766 */ showVideoUi(int videoState, int callState)767 private void showVideoUi(int videoState, int callState) { 768 VideoCallUi ui = getUi(); 769 if (ui == null) { 770 Log.e(this, "showVideoUi, VideoCallUi is null returning"); 771 return; 772 } 773 boolean isPaused = VideoProfile.isPaused(videoState); 774 boolean isCallActive = callState == Call.State.ACTIVE; 775 if (VideoProfile.isBidirectional(videoState)) { 776 ui.showVideoViews(true, !isPaused && isCallActive); 777 } else if (VideoProfile.isTransmissionEnabled(videoState)) { 778 ui.showVideoViews(true, false); 779 } else if (VideoProfile.isReceptionEnabled(videoState)) { 780 ui.showVideoViews(false, !isPaused && isCallActive); 781 loadProfilePhotoAsync(); 782 } else { 783 ui.hideVideoUi(); 784 } 785 786 InCallPresenter.getInstance().enableScreenTimeout( 787 VideoProfile.isAudioOnly(videoState)); 788 } 789 790 /** 791 * Handles peer video pause state changes. 792 * 793 * @param call The call which paused or un-pausedvideo transmission. 794 * @param paused {@code True} when the video transmission is paused, {@code false} when video 795 * transmission resumes. 796 */ 797 @Override onPeerPauseStateChanged(Call call, boolean paused)798 public void onPeerPauseStateChanged(Call call, boolean paused) { 799 if (!call.equals(mPrimaryCall)) { 800 return; 801 } 802 803 // TODO(vt): Show/hide the peer contact photo. 804 } 805 806 /** 807 * Handles peer video dimension changes. 808 * 809 * @param call The call which experienced a peer video dimension change. 810 * @param width The new peer video width . 811 * @param height The new peer video height. 812 */ 813 @Override onUpdatePeerDimensions(Call call, int width, int height)814 public void onUpdatePeerDimensions(Call call, int width, int height) { 815 Log.d(this, "onUpdatePeerDimensions: width= " + width + " height= " + height); 816 VideoCallUi ui = getUi(); 817 if (ui == null) { 818 Log.e(this, "VideoCallUi is null. Bail out"); 819 return; 820 } 821 if (!call.equals(mPrimaryCall)) { 822 Log.e(this, "Current call is not equal to primary call. Bail out"); 823 return; 824 } 825 826 // Change size of display surface to match the peer aspect ratio 827 if (width > 0 && height > 0) { 828 setDisplayVideoSize(width, height); 829 } 830 } 831 832 /** 833 * Handles any video quality changes in the call. 834 * 835 * @param call The call which experienced a video quality change. 836 * @param videoQuality The new video call quality. 837 */ 838 @Override onVideoQualityChanged(Call call, int videoQuality)839 public void onVideoQualityChanged(Call call, int videoQuality) { 840 // No-op 841 } 842 843 /** 844 * Handles a change to the dimensions of the local camera. Receiving the camera capabilities 845 * triggers the creation of the video 846 * 847 * @param call The call which experienced the camera dimension change. 848 * @param width The new camera video width. 849 * @param height The new camera video height. 850 */ 851 @Override onCameraDimensionsChange(Call call, int width, int height)852 public void onCameraDimensionsChange(Call call, int width, int height) { 853 Log.d(this, "onCameraDimensionsChange call=" + call + " width=" + width + " height=" 854 + height); 855 VideoCallUi ui = getUi(); 856 if (ui == null) { 857 Log.e(this, "onCameraDimensionsChange ui is null"); 858 return; 859 } 860 861 if (!call.equals(mPrimaryCall)) { 862 Log.e(this, "Call is not primary call"); 863 return; 864 } 865 866 mPreviewSurfaceState = PreviewSurfaceState.CAPABILITIES_RECEIVED; 867 changePreviewDimensions(width, height); 868 869 // Check if the preview surface is ready yet; if it is, set it on the {@code VideoCall}. 870 // If it not yet ready, it will be set when when creation completes. 871 if (ui.isPreviewVideoSurfaceCreated()) { 872 mPreviewSurfaceState = PreviewSurfaceState.SURFACE_SET; 873 mVideoCall.setPreviewSurface(ui.getPreviewVideoSurface()); 874 } 875 } 876 877 /** 878 * Changes the dimensions of the preview surface. 879 * 880 * @param width The new width. 881 * @param height The new height. 882 */ changePreviewDimensions(int width, int height)883 private void changePreviewDimensions(int width, int height) { 884 VideoCallUi ui = getUi(); 885 if (ui == null) { 886 return; 887 } 888 889 // Resize the surface used to display the preview video 890 ui.setPreviewSurfaceSize(width, height); 891 892 // Configure the preview surface to the correct aspect ratio. 893 float aspectRatio = 1.0f; 894 if (width > 0 && height > 0) { 895 aspectRatio = (float) width / (float) height; 896 } 897 898 // Resize the textureview housing the preview video and rotate it appropriately based on 899 // the device orientation 900 setPreviewSize(mDeviceOrientation, aspectRatio); 901 } 902 903 /** 904 * Called when call session event is raised. 905 * 906 * @param event The call session event. 907 */ 908 @Override onCallSessionEvent(int event)909 public void onCallSessionEvent(int event) { 910 StringBuilder sb = new StringBuilder(); 911 sb.append("onCallSessionEvent = "); 912 913 switch (event) { 914 case Connection.VideoProvider.SESSION_EVENT_RX_PAUSE: 915 sb.append("rx_pause"); 916 break; 917 case Connection.VideoProvider.SESSION_EVENT_RX_RESUME: 918 sb.append("rx_resume"); 919 break; 920 case Connection.VideoProvider.SESSION_EVENT_CAMERA_FAILURE: 921 sb.append("camera_failure"); 922 break; 923 case Connection.VideoProvider.SESSION_EVENT_CAMERA_READY: 924 sb.append("camera_ready"); 925 break; 926 default: 927 sb.append("unknown event = "); 928 sb.append(event); 929 break; 930 } 931 Log.d(this, sb.toString()); 932 } 933 934 /** 935 * Handles a change to the call data usage 936 * 937 * @param dataUsage call data usage value 938 */ 939 @Override onCallDataUsageChange(long dataUsage)940 public void onCallDataUsageChange(long dataUsage) { 941 Log.d(this, "onCallDataUsageChange dataUsage=" + dataUsage); 942 } 943 944 /** 945 * Handles changes to the device orientation. 946 * 947 * @param orientation The device orientation (one of: {@link Surface#ROTATION_0}, 948 * {@link Surface#ROTATION_90}, {@link Surface#ROTATION_180}, 949 * {@link Surface#ROTATION_270}). 950 */ 951 @Override onDeviceOrientationChanged(int orientation)952 public void onDeviceOrientationChanged(int orientation) { 953 mDeviceOrientation = orientation; 954 Point previewDimensions = getUi().getPreviewSize(); 955 if (previewDimensions == null) { 956 return; 957 } 958 Log.d(this, "onDeviceOrientationChanged: orientation=" + orientation + " size: " 959 + previewDimensions); 960 changePreviewDimensions(previewDimensions.x, previewDimensions.y); 961 } 962 963 @Override onUpgradeToVideoRequest(Call call, int videoState)964 public void onUpgradeToVideoRequest(Call call, int videoState) { 965 Log.d(this, "onUpgradeToVideoRequest call = " + call + " new video state = " + videoState); 966 if (mPrimaryCall == null || !Call.areSame(mPrimaryCall, call)) { 967 Log.w(this, "UpgradeToVideoRequest received for non-primary call"); 968 } 969 970 if (call == null) { 971 return; 972 } 973 974 call.setSessionModificationTo(videoState); 975 } 976 977 @Override onUpgradeToVideoSuccess(Call call)978 public void onUpgradeToVideoSuccess(Call call) { 979 Log.d(this, "onUpgradeToVideoSuccess call=" + call); 980 if (mPrimaryCall == null || !Call.areSame(mPrimaryCall, call)) { 981 Log.w(this, "UpgradeToVideoSuccess received for non-primary call"); 982 } 983 984 if (call == null) { 985 return; 986 } 987 } 988 989 @Override onUpgradeToVideoFail(int status, Call call)990 public void onUpgradeToVideoFail(int status, Call call) { 991 Log.d(this, "onUpgradeToVideoFail call=" + call); 992 if (mPrimaryCall == null || !Call.areSame(mPrimaryCall, call)) { 993 Log.w(this, "UpgradeToVideoFail received for non-primary call"); 994 } 995 996 if (call == null) { 997 return; 998 } 999 } 1000 1001 @Override onDowngradeToAudio(Call call)1002 public void onDowngradeToAudio(Call call) { 1003 call.setSessionModificationState(Call.SessionModificationState.NO_REQUEST); 1004 // exit video mode 1005 exitVideoMode(); 1006 } 1007 1008 /** 1009 * Sets the preview surface size based on the current device orientation. 1010 * 1011 * @param orientation The device orientation (one of: {@link Surface#ROTATION_0}, 1012 * {@link Surface#ROTATION_90}, {@link Surface#ROTATION_180}, 1013 * {@link Surface#ROTATION_270}). 1014 * @param aspectRatio The aspect ratio of the camera (width / height). 1015 */ setPreviewSize(int orientation, float aspectRatio)1016 private void setPreviewSize(int orientation, float aspectRatio) { 1017 VideoCallUi ui = getUi(); 1018 if (ui == null) { 1019 return; 1020 } 1021 1022 int height; 1023 int width; 1024 1025 if (orientation == Surface.ROTATION_90 || orientation == Surface.ROTATION_270) { 1026 // Landscape or reverse landscape orientation. 1027 width = (int) (mMinimumVideoDimension * aspectRatio); 1028 height = (int) mMinimumVideoDimension; 1029 } else { 1030 // Portrait or reverse portrait orientation. 1031 width = (int) mMinimumVideoDimension; 1032 height = (int) (mMinimumVideoDimension * aspectRatio); 1033 } 1034 ui.setPreviewSize(width, height); 1035 } 1036 1037 /** 1038 * Sets the display video surface size based on peer width and height 1039 * 1040 * @param width peer width 1041 * @param height peer height 1042 */ setDisplayVideoSize(int width, int height)1043 private void setDisplayVideoSize(int width, int height) { 1044 Log.d(this, "setDisplayVideoSize:Received peer width=" + width + " peer height=" + height); 1045 VideoCallUi ui = getUi(); 1046 if (ui == null) { 1047 return; 1048 } 1049 1050 // Get current display size 1051 Point size = ui.getScreenSize(); 1052 Log.d("VideoCallPresenter", "setDisplayVideoSize: windowmgr width=" + size.x 1053 + " windowmgr height=" + size.y); 1054 if (size.y * width > size.x * height) { 1055 // current display height is too much. Correct it 1056 size.y = (int) (size.x * height / width); 1057 } else if (size.y * width < size.x * height) { 1058 // current display width is too much. Correct it 1059 size.x = (int) (size.y * width / height); 1060 } 1061 ui.setDisplayVideoSize(size.x, size.y); 1062 } 1063 1064 /** 1065 * Exits fullscreen mode if the current call context has changed to a non-video call. 1066 * 1067 * @param call The call. 1068 */ maybeExitFullscreen(Call call)1069 protected void maybeExitFullscreen(Call call) { 1070 if (call == null) { 1071 return; 1072 } 1073 1074 if (!CallUtils.isVideoCall(call) || call.getState() == Call.State.INCOMING) { 1075 InCallPresenter.getInstance().setFullScreen(false); 1076 } 1077 } 1078 1079 /** 1080 * Schedules auto-entering of fullscreen mode. 1081 * Will not enter full screen mode if any of the following conditions are met: 1082 * 1. No call 1083 * 2. Call is not active 1084 * 3. Call is not video call 1085 * 4. Already in fullscreen mode 1086 * 1087 * @param call The current call. 1088 */ maybeAutoEnterFullscreen(Call call)1089 protected void maybeAutoEnterFullscreen(Call call) { 1090 if (!mIsAutoFullscreenEnabled) { 1091 return; 1092 } 1093 1094 if (call == null || ( 1095 call != null && (call.getState() != Call.State.ACTIVE || 1096 !CallUtils.isVideoCall(call)) || 1097 InCallPresenter.getInstance().isFullscreen())) { 1098 // Ensure any previously scheduled attempt to enter fullscreen is cancelled. 1099 cancelAutoFullScreen(); 1100 return; 1101 } 1102 1103 if (mAutoFullScreenPending) { 1104 Log.v(this, "maybeAutoEnterFullscreen : already pending."); 1105 return; 1106 } 1107 Log.v(this, "maybeAutoEnterFullscreen : scheduled"); 1108 mAutoFullScreenPending = true; 1109 mHandler.postDelayed(mAutoFullscreenRunnable, mAutoFullscreenTimeoutMillis); 1110 } 1111 1112 /** 1113 * Cancels pending auto fullscreen mode. 1114 */ cancelAutoFullScreen()1115 public void cancelAutoFullScreen() { 1116 if (!mAutoFullScreenPending) { 1117 Log.v(this, "cancelAutoFullScreen : none pending."); 1118 return; 1119 } 1120 Log.v(this, "cancelAutoFullScreen : cancelling pending"); 1121 mAutoFullScreenPending = false; 1122 } 1123 isAudioRouteEnabled(int audioRoute, int audioRouteMask)1124 private static boolean isAudioRouteEnabled(int audioRoute, int audioRouteMask) { 1125 return ((audioRoute & audioRouteMask) != 0); 1126 } 1127 updateCameraSelection(Call call)1128 private static void updateCameraSelection(Call call) { 1129 Log.d(TAG, "updateCameraSelection: call=" + call); 1130 Log.d(TAG, "updateCameraSelection: call=" + toSimpleString(call)); 1131 1132 final Call activeCall = CallList.getInstance().getActiveCall(); 1133 int cameraDir = Call.VideoSettings.CAMERA_DIRECTION_UNKNOWN; 1134 1135 // this function should never be called with null call object, however if it happens we 1136 // should handle it gracefully. 1137 if (call == null) { 1138 cameraDir = Call.VideoSettings.CAMERA_DIRECTION_UNKNOWN; 1139 com.android.incallui.Log.e(TAG, "updateCameraSelection: Call object is null." 1140 + " Setting camera direction to default value (CAMERA_DIRECTION_UNKNOWN)"); 1141 } 1142 1143 // Clear camera direction if this is not a video call. 1144 else if (CallUtils.isAudioCall(call)) { 1145 cameraDir = Call.VideoSettings.CAMERA_DIRECTION_UNKNOWN; 1146 call.getVideoSettings().setCameraDir(cameraDir); 1147 } 1148 1149 // If this is a waiting video call, default to active call's camera, 1150 // since we don't want to change the current camera for waiting call 1151 // without user's permission. 1152 else if (CallUtils.isVideoCall(activeCall) && CallUtils.isIncomingVideoCall(call)) { 1153 cameraDir = activeCall.getVideoSettings().getCameraDir(); 1154 } 1155 1156 // Infer the camera direction from the video state and store it, 1157 // if this is an outgoing video call. 1158 else if (CallUtils.isOutgoingVideoCall(call) && !isCameraDirectionSet(call) ) { 1159 cameraDir = toCameraDirection(call.getVideoState()); 1160 call.getVideoSettings().setCameraDir(cameraDir); 1161 } 1162 1163 // Use the stored camera dir if this is an outgoing video call for which camera direction 1164 // is set. 1165 else if (CallUtils.isOutgoingVideoCall(call)) { 1166 cameraDir = call.getVideoSettings().getCameraDir(); 1167 } 1168 1169 // Infer the camera direction from the video state and store it, 1170 // if this is an active video call and camera direction is not set. 1171 else if (CallUtils.isActiveVideoCall(call) && !isCameraDirectionSet(call)) { 1172 cameraDir = toCameraDirection(call.getVideoState()); 1173 call.getVideoSettings().setCameraDir(cameraDir); 1174 } 1175 1176 // Use the stored camera dir if this is an active video call for which camera direction 1177 // is set. 1178 else if (CallUtils.isActiveVideoCall(call)) { 1179 cameraDir = call.getVideoSettings().getCameraDir(); 1180 } 1181 1182 // For all other cases infer the camera direction but don't store it in the call object. 1183 else { 1184 cameraDir = toCameraDirection(call.getVideoState()); 1185 } 1186 1187 com.android.incallui.Log.d(TAG, "updateCameraSelection: Setting camera direction to " + 1188 cameraDir + " Call=" + call); 1189 final InCallCameraManager cameraManager = InCallPresenter.getInstance(). 1190 getInCallCameraManager(); 1191 cameraManager.setUseFrontFacingCamera(cameraDir == 1192 Call.VideoSettings.CAMERA_DIRECTION_FRONT_FACING); 1193 } 1194 toCameraDirection(int videoState)1195 private static int toCameraDirection(int videoState) { 1196 return VideoProfile.isTransmissionEnabled(videoState) && 1197 !VideoProfile.isBidirectional(videoState) 1198 ? Call.VideoSettings.CAMERA_DIRECTION_BACK_FACING 1199 : Call.VideoSettings.CAMERA_DIRECTION_FRONT_FACING; 1200 } 1201 isCameraDirectionSet(Call call)1202 private static boolean isCameraDirectionSet(Call call) { 1203 return CallUtils.isVideoCall(call) && call.getVideoSettings().getCameraDir() 1204 != Call.VideoSettings.CAMERA_DIRECTION_UNKNOWN; 1205 } 1206 toSimpleString(Call call)1207 private static String toSimpleString(Call call) { 1208 return call == null ? null : call.toSimpleString(); 1209 } 1210 1211 /** 1212 * Starts an asynchronous load of the user's profile photo. 1213 */ loadProfilePhotoAsync()1214 public void loadProfilePhotoAsync() { 1215 final VideoCallUi ui = getUi(); 1216 if (ui == null) { 1217 return; 1218 } 1219 1220 final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() { 1221 /** 1222 * Performs asynchronous load of the user profile information. 1223 * 1224 * @param params The parameters of the task. 1225 * 1226 * @return {@code null}. 1227 */ 1228 @Override 1229 protected Void doInBackground(Void... params) { 1230 if (mProfileInfo == null) { 1231 // Try and read the photo URI from the local profile. 1232 mProfileInfo = new ContactInfoCache.ContactCacheEntry(); 1233 final Cursor cursor = mContext.getContentResolver().query( 1234 ContactsContract.Profile.CONTENT_URI, new String[]{ 1235 ContactsContract.CommonDataKinds.Phone._ID, 1236 ContactsContract.CommonDataKinds.Phone.PHOTO_URI, 1237 ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY, 1238 ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, 1239 }, null, null, null); 1240 if (cursor != null) { 1241 try { 1242 if (cursor.moveToFirst()) { 1243 mProfileInfo.lookupKey = cursor.getString(cursor.getColumnIndex( 1244 ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY)); 1245 String photoUri = cursor.getString(cursor.getColumnIndex( 1246 ContactsContract.CommonDataKinds.Phone.PHOTO_URI)); 1247 mProfileInfo.displayPhotoUri = photoUri == null ? null 1248 : Uri.parse(photoUri); 1249 mProfileInfo.name = cursor.getString(cursor.getColumnIndex( 1250 ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); 1251 } 1252 } finally { 1253 cursor.close(); 1254 } 1255 } 1256 } 1257 return null; 1258 } 1259 1260 @Override 1261 protected void onPostExecute(Void result) { 1262 // If user profile information was found, issue an async request to load the user's 1263 // profile photo. 1264 if (mProfileInfo != null) { 1265 if (mContactPhotoManager == null) { 1266 mContactPhotoManager = ContactPhotoManager.getInstance(mContext); 1267 } 1268 ContactPhotoManager.DefaultImageRequest imageRequest = (mProfileInfo != null) 1269 ? null : 1270 new ContactPhotoManager.DefaultImageRequest(mProfileInfo.name, 1271 mProfileInfo.lookupKey, false /* isCircularPhoto */); 1272 1273 ImageView photoView = ui.getPreviewPhotoView(); 1274 if (photoView == null) { 1275 return; 1276 } 1277 mContactPhotoManager.loadDirectoryPhoto(photoView, 1278 mProfileInfo.displayPhotoUri, 1279 false /* darkTheme */, false /* isCircular */, imageRequest); 1280 } 1281 } 1282 }; 1283 1284 task.execute(); 1285 } 1286 1287 /** 1288 * Defines the VideoCallUI interactions. 1289 */ 1290 public interface VideoCallUi extends Ui { showVideoViews(boolean showPreview, boolean showIncoming)1291 void showVideoViews(boolean showPreview, boolean showIncoming); hideVideoUi()1292 void hideVideoUi(); isDisplayVideoSurfaceCreated()1293 boolean isDisplayVideoSurfaceCreated(); isPreviewVideoSurfaceCreated()1294 boolean isPreviewVideoSurfaceCreated(); getDisplayVideoSurface()1295 Surface getDisplayVideoSurface(); getPreviewVideoSurface()1296 Surface getPreviewVideoSurface(); getCurrentRotation()1297 int getCurrentRotation(); setPreviewSize(int width, int height)1298 void setPreviewSize(int width, int height); setPreviewSurfaceSize(int width, int height)1299 void setPreviewSurfaceSize(int width, int height); setDisplayVideoSize(int width, int height)1300 void setDisplayVideoSize(int width, int height); getScreenSize()1301 Point getScreenSize(); getPreviewSize()1302 Point getPreviewSize(); cleanupSurfaces()1303 void cleanupSurfaces(); getPreviewPhotoView()1304 ImageView getPreviewPhotoView(); 1305 } 1306 } 1307