• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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