Home | History | Annotate | Download | only in incallui
      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      */
    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
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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
    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
    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
    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 
    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 
    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 
    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 
    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 
    571     private boolean isVideoMode() {
    572         return mIsVideoMode;
    573     }
    574 
    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
    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 
    614     private void updateVideoCall(Call call) {
    615         checkForVideoCallChange(call);
    616         checkForVideoStateChange(call);
    617         checkForCallStateChange(call);
    618         checkForOrientationAllowedChange(call);
    619     }
    620 
    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      */
    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      */
    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 
    667     private static boolean isCameraRequired(int videoState) {
    668         return VideoProfile.isBidirectional(videoState)
    669                 || VideoProfile.isTransmissionEnabled(videoState);
    670     }
    671 
    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      */
    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 
    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      */
    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      */
    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      */
    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      */
    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
    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
    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
    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
    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      */
    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
    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
    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
    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      */
   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      */
   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      */
   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      */
   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      */
   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 
   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 
   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 
   1196     private static boolean isCameraDirectionSet(Call call) {
   1197         return VideoUtils.isVideoCall(call) && call.getVideoSettings().getCameraDir()
   1198                     != Call.VideoSettings.CAMERA_DIRECTION_UNKNOWN;
   1199     }
   1200 
   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      */
   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 {
   1289         void showVideoViews(boolean showPreview, boolean showIncoming);
   1290         void hideVideoUi();
   1291         boolean isDisplayVideoSurfaceCreated();
   1292         boolean isPreviewVideoSurfaceCreated();
   1293         Surface getDisplayVideoSurface();
   1294         Surface getPreviewVideoSurface();
   1295         int getCurrentRotation();
   1296         void setPreviewSize(int width, int height);
   1297         void setPreviewSurfaceSize(int width, int height);
   1298         void setDisplayVideoSize(int width, int height);
   1299         Point getScreenSize();
   1300         Point getPreviewSize();
   1301         void cleanupSurfaces();
   1302         ImageView getPreviewPhotoView();
   1303         void adjustPreviewLocation(boolean shiftUp, int offset);
   1304         void setPreviewRotation(int orientation);
   1305     }
   1306 }
   1307