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