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