IMS-VT: FR36165 - UI renders after receiving video frame

- Show far end video after receiving the first video frame

- IMS notifies us when it receives the first video frame
  through the PLAYER_START event. Show incoming video
  once we receive this event

- When frames stop transmitting, IMS notifies us through
  the PLAYER_STOP event. Hide the incoming video once
  we receive this event

Change-Id: Ibdd001679d08cd5dbefd2f944ac97597897bcc51
CRs-Fixed: 1051225
diff --git a/InCallUI/src/com/android/incallui/InCallVideoCallCallbackNotifier.java b/InCallUI/src/com/android/incallui/InCallVideoCallCallbackNotifier.java
index d65640b..1a24b16 100644
--- a/InCallUI/src/com/android/incallui/InCallVideoCallCallbackNotifier.java
+++ b/InCallUI/src/com/android/incallui/InCallVideoCallCallbackNotifier.java
@@ -46,6 +46,14 @@
     private final Set<SurfaceChangeListener> mSurfaceChangeListeners = Collections.newSetFromMap(
             new ConcurrentHashMap<SurfaceChangeListener, Boolean>(8, 0.9f, 1));
 
+    /* Invalid call session event */
+    public static final int CALL_SESSION_INVALID_EVENT = -1;
+
+    /** Cache the call session event for cases where the call is in background and listeners
+     * are unregistered.
+     */
+    private int mCallSessionEvent = CALL_SESSION_INVALID_EVENT;
+
     /**
      * Static singleton accessor method.
      */
@@ -91,6 +99,22 @@
     }
 
     /**
+     * Adds a new {@link VideoEventListener} and notifies the entity that registered
+     * if flag notify is true.
+     *
+     * @param listener The listener.
+     * @param notify true or false
+     */
+    public void addVideoEventListener(VideoEventListener listener, boolean notify) {
+        addVideoEventListener(listener);
+
+        // Notify registered listeners of cached call session event if it's a valid value
+        if (notify && mCallSessionEvent != CALL_SESSION_INVALID_EVENT) {
+            callSessionEvent(mCallSessionEvent);
+        }
+    }
+
+    /**
      * Remove a {@link VideoEventListener}.
      *
      * @param listener The listener.
@@ -151,8 +175,9 @@
      * @param event The call session event.
      */
     public void callSessionEvent(int event) {
+        mCallSessionEvent = event;
         for (VideoEventListener listener : mVideoEventListeners) {
-            listener.onCallSessionEvent(event);
+            listener.onCallSessionEvent(mCallSessionEvent);
         }
     }
 
diff --git a/InCallUI/src/com/android/incallui/VideoCallPresenter.java b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
index cb5f43b..fabdc99 100644
--- a/InCallUI/src/com/android/incallui/VideoCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
@@ -205,6 +205,14 @@
     private int mActivityOrientationMode = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
     /**
+     * Determines if the incoming video is available. If the call session resume event has been
+     * received (i.e PLAYER_START has been received from lower layers), incoming video is
+     * available. If the call session pause event has been received (i.e PLAYER_STOP has been
+     * received from lower layers), incoming video is not available.
+     */
+    private static boolean mIsIncomingVideoAvailable = false;
+
+    /**
      * Initializes the presenter.
      *
      * @param context The current context.
@@ -247,7 +255,6 @@
 
         // Register for surface and video events from {@link InCallVideoCallListener}s.
         InCallVideoCallCallbackNotifier.getInstance().addSurfaceChangeListener(this);
-        InCallVideoCallCallbackNotifier.getInstance().addVideoEventListener(this);
         InCallUiStateNotifier.getInstance().addListener(this);
         mCurrentVideoState = VideoProfile.STATE_AUDIO_ONLY;
         mCurrentCallState = Call.State.INVALID;
@@ -255,6 +262,8 @@
         final InCallPresenter.InCallState inCallState =
              InCallPresenter.getInstance().getInCallState();
         onStateChange(inCallState, inCallState, CallList.getInstance());
+        InCallVideoCallCallbackNotifier.getInstance().addVideoEventListener(this,
+                VideoUtils.isVideoCall(mCurrentVideoState));
     }
 
     /**
@@ -310,6 +319,12 @@
             }
         } else if (surface == VideoCallFragment.SURFACE_DISPLAY) {
             mVideoCall.setDisplaySurface(ui.getDisplayVideoSurface());
+
+            // Show/hide the incoming video once surface is created based on
+            // whether PLAYER_START event has been received or not. Since we
+            // start with showing incoming video by default for surface creation,
+            // we need to make sure we hide it once surface is available.
+            showVideoUi(mCurrentVideoState, mCurrentCallState);
         }
     }
 
@@ -768,7 +783,10 @@
     /**
      * Based on the current video state and call state, show or hide the incoming and
      * outgoing video surfaces.  The outgoing video surface is shown any time video is transmitting.
-     * The incoming video surface is shown whenever the video is un-paused and active.
+     * The incoming video surface is shown whenever the video is un-paused and active and incoming
+     * video is available. If display surface has not been created and video reception is enabled,
+     * we override the value returned by showIncomingVideo and show the incoming video so surface
+     * creation is enabled
      *
      * @param videoState The video state.
      * @param callState The call state.
@@ -779,14 +797,19 @@
             Log.e(this, "showVideoUi, VideoCallUi is null returning");
             return;
         }
-        boolean showIncomingVideo = showIncomingVideo(videoState, callState);
+
+        final boolean isDisplaySurfaceCreated = ui.isDisplayVideoSurfaceCreated();
+        final boolean isVideoReceptionEnabled = VideoProfile.isReceptionEnabled(videoState);
+        boolean showIncomingVideo = showIncomingVideo(videoState, callState) ||
+                (!isDisplaySurfaceCreated && isVideoReceptionEnabled);
         boolean showOutgoingVideo = showOutgoingVideo(videoState);
+
         Log.v(this, "showVideoUi : showIncoming = " + showIncomingVideo + " showOutgoing = "
                 + showOutgoingVideo);
         if (showIncomingVideo || showOutgoingVideo) {
             ui.showVideoViews(showOutgoingVideo, showIncomingVideo);
 
-            if (VideoProfile.isReceptionEnabled(videoState)) {
+            if (isVideoReceptionEnabled) {
                 loadProfilePhotoAsync();
             }
         } else {
@@ -799,8 +822,9 @@
 
     /**
      * Determines if the incoming video surface should be shown based on the current videoState and
-     * callState.  The video surface is shown when incoming video is not paused, the call is active
-     * or dialing and video reception is enabled.
+     * callState.  The video surface is shown when video reception is enabled AND either incoming
+     * video is not paused, the call is active or dialing, incoming video is available
+     * (i.e PLAYER_START event has been raised by lower layers)
      *
      * @param videoState The current video state.
      * @param callState The current call state.
@@ -818,7 +842,7 @@
                 callState == Call.State.CONNECTING;
 
         return !isPaused && (isCallActive || isCallOutgoing) &&
-                VideoProfile.isReceptionEnabled(videoState);
+                VideoProfile.isReceptionEnabled(videoState) && mIsIncomingVideoAvailable;
     }
 
     /**
@@ -996,10 +1020,11 @@
 
         switch (event) {
             case Connection.VideoProvider.SESSION_EVENT_RX_PAUSE:
-                sb.append("rx_pause");
-                break;
             case Connection.VideoProvider.SESSION_EVENT_RX_RESUME:
-                sb.append("rx_resume");
+                mIsIncomingVideoAvailable =
+                    event == Connection.VideoProvider.SESSION_EVENT_RX_RESUME;
+                showVideoUi(mCurrentVideoState, mCurrentCallState);
+                sb.append(mIsIncomingVideoAvailable ? "rx_resume" : "rx_pause");
                 break;
             case Connection.VideoProvider.SESSION_EVENT_CAMERA_FAILURE:
                 sb.append("camera_failure");