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