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