Merge "IMS: Correctly set preview surface dimensions" into atel.lnx.2.0-dev
diff --git a/InCallUI/res/values/qtiarray.xml b/InCallUI/res/values/qtiarray.xml
index 3e7e1e8..1edadb6 100644
--- a/InCallUI/res/values/qtiarray.xml
+++ b/InCallUI/res/values/qtiarray.xml
@@ -51,12 +51,16 @@
         <item>@null</item>
         <item>@string/description_target_decline</item>
         <item>@string/description_target_answer_audio_call</item>
+        <item>@string/description_target_accept_upgrade_to_video_transmit_request</item>
+        <item>@string/description_target_accept_upgrade_to_video_receive_request</item>
     </array>
     <array name="qti_incoming_call_widget_video_without_sms_direction_descriptions">
         <item>@string/description_direction_right</item>
         <item>@null</item>
         <item>@string/description_direction_left</item>
         <item>@string/description_direction_down</item>
+        <item>@null</item>
+        <item>@null</item>
     </array>
 
     <!-- For video transmit(Tx), if respond via SMS is disabled:
@@ -123,12 +127,16 @@
         <item>@string/description_target_send_sms</item>
         <item>@string/description_target_decline</item>
         <item>@string/description_target_answer_audio_call</item>
+        <item>@string/description_target_accept_upgrade_to_video_transmit_request</item>
+        <item>@string/description_target_accept_upgrade_to_video_receive_request</item>
     </array>
     <array name="qti_incoming_call_widget_video_with_sms_direction_descriptions">
         <item>@string/description_direction_right</item>
         <item>@string/description_direction_up</item>
         <item>@string/description_direction_left</item>
         <item>@string/description_direction_down</item>
+        <item>@null</item>
+        <item>@null</item>
     </array>
 
     <!-- For video transmit(Tx) calls, if respond via SMS is enabled:
@@ -192,14 +200,16 @@
 
     <array name="qti_incoming_call_widget_video_request_target_descriptions">
         <item>@string/description_target_accept_upgrade_to_video_request</item>
-        <item>@null</item>
         <item>@string/description_target_decline_upgrade_to_video_request</item>
-        <item>@null</item>"
+        <item>@string/description_target_answer_audio_call</item>
+        <item>@string/description_target_accept_upgrade_to_video_transmit_request</item>
+        <item>@string/description_target_accept_upgrade_to_video_receive_request</item>
     </array>
     <array name="qti_incoming_call_widget_video_request_target_direction_descriptions">
         <item>@string/description_direction_right</item>
-        <item>@null</item>
+        <item>@string/description_direction_up</item>
         <item>@string/description_direction_left</item>
+        <item>@string/description_direction_down</item>
         <item>@null</item>
     </array>
 
diff --git a/InCallUI/res/values/qtistrings.xml b/InCallUI/res/values/qtistrings.xml
index 18873cc..9ef2bd8 100644
--- a/InCallUI/res/values/qtistrings.xml
+++ b/InCallUI/res/values/qtistrings.xml
@@ -143,6 +143,8 @@
     <string name="session_modify_cause_downgrade_lipsync">Call downgraded due to lipsync</string>
     <!-- Session modify cause code downgrade generic error -->
     <string name="session_modify_cause_downgrade_generic_error">Call downgraded due to generic error</string>
+    <!-- Session modify cause code downgrade generic -->
+    <string name="session_modify_cause_downgrade_generic">Video call changed to voice call</string>
 
     <!-- In-call screen: status label for an incoming conference call [CHAR LIMIT=25] -->
     <string name="card_title_incoming_conf_call">Incoming conference call</string>
diff --git a/InCallUI/src/com/android/incallui/Call.java b/InCallUI/src/com/android/incallui/Call.java
index feaf5ee..c8906ed 100644
--- a/InCallUI/src/com/android/incallui/Call.java
+++ b/InCallUI/src/com/android/incallui/Call.java
@@ -395,7 +395,7 @@
     private int mRequestedVideoState = VideoProfile.STATE_AUDIO_ONLY;
 
     private InCallVideoCallCallback mVideoCallCallback;
-    private boolean mIsVideoCallCallbackRegistered;
+    private VideoCall mRegisteredVideoCall;
     private String mChildNumber;
     private String mLastForwardedNumber;
     private String mCallSubject;
@@ -489,7 +489,7 @@
                 mVideoCallCallback = new InCallVideoCallCallback(this);
             }
             mTelecomCall.getVideoCall().registerCallback(mVideoCallCallback);
-            mIsVideoCallCallbackRegistered = true;
+            mRegisteredVideoCall = mTelecomCall.getVideoCall();
         }
 
         mChildCallIds.clear();
@@ -845,7 +845,7 @@
      *      callback on the {@link VideoCall}.
      */
     public VideoCall getVideoCall() {
-        return mTelecomCall == null || !mIsVideoCallCallbackRegistered ? null
+        return mTelecomCall == null || (mRegisteredVideoCall != mTelecomCall.getVideoCall()) ? null
                 : mTelecomCall.getVideoCall();
     }
 
diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
index de49fff..91a8e0b 100644
--- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
@@ -355,9 +355,11 @@
         int currUnpausedVideoState = VideoUtils.getUnPausedVideoState(currVideoState);
         currUnpausedVideoState |= VideoProfile.STATE_BIDIRECTIONAL;
 
-        VideoProfile videoProfile = new VideoProfile(currUnpausedVideoState);
-        videoCall.sendSessionModifyRequest(videoProfile);
-        mCall.setSessionModificationState(Call.SessionModificationState.WAITING_FOR_RESPONSE);
+        if (!InCallLowBatteryListener.getInstance().onChangeToVideoCall(mCall)) {
+            VideoProfile videoProfile = new VideoProfile(currUnpausedVideoState);
+            videoCall.sendSessionModifyRequest(videoProfile);
+            mCall.setSessionModificationState(Call.SessionModificationState.WAITING_FOR_RESPONSE);
+        }
 
         if (QtiCallUtils.useCustomVideoUi(context)) {
             InCallAudioManager.getInstance().onModifyCallClicked(mCall,
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index 63d98f4..496c349 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -60,6 +60,8 @@
 
 import java.lang.ref.WeakReference;
 
+import org.codeaurora.ims.utils.QtiImsExtUtils;
+
 import static com.android.contacts.common.compat.CallSdkCompat.Details.PROPERTY_ENTERPRISE_CALL;
 /**
  * Presenter for the Call Card Fragment.
@@ -956,9 +958,13 @@
             return;
         }
 
-        final boolean notUpdateSecondary = mSecondary.getState() == Call.State.ACTIVE
-                && !mSecondary.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD)
-                && !mSecondary.can(android.telecom.Call.Details.CAPABILITY_HOLD);
+        boolean notUpdateSecondary = false;
+        if (QtiImsExtUtils.isCarrierConfigEnabled(mContext,
+                "hide_held_call_when_end_conf_call")) {
+            notUpdateSecondary = mSecondary.getState() == Call.State.ACTIVE
+                    && !mSecondary.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD)
+                    && !mSecondary.can(android.telecom.Call.Details.CAPABILITY_HOLD);
+        }
         Log.d(TAG, "notUpdateSecondary:" + notUpdateSecondary);
         if (mSecondary.isConferenceCall()) {
             ui.setSecondary(
diff --git a/InCallUI/src/com/android/incallui/InCallLowBatteryListener.java b/InCallUI/src/com/android/incallui/InCallLowBatteryListener.java
index c413b07..d835616 100644
--- a/InCallUI/src/com/android/incallui/InCallLowBatteryListener.java
+++ b/InCallUI/src/com/android/incallui/InCallLowBatteryListener.java
@@ -38,6 +38,7 @@
 import android.content.DialogInterface.OnDismissListener;
 import android.content.DialogInterface.OnKeyListener;
 import android.os.Bundle;
+import android.telecom.InCallService.VideoCall;
 import android.telecom.VideoProfile;
 import android.view.KeyEvent;
 import android.view.WindowManager;
@@ -73,7 +74,7 @@
  * signalling that low battery indication for the call can be processed now.
  */
 public class InCallLowBatteryListener implements CallList.Listener, InCallDetailsListener,
-        InCallUiListener {
+        InCallUiListener, CallList.CallUpdateListener {
 
     private static InCallLowBatteryListener sInCallLowBatteryListener;
     private PrimaryCallTracker mPrimaryCallTracker;
@@ -168,6 +169,11 @@
             return;
         }
 
+        /* Clean up callUpdateListener in case call is terminated during MT upgrade
+           request is in progress. */
+        if (isIncomingUpgradeReq(call.getSessionModificationState())) {
+            CallList.getInstance().removeCallUpdateListener(call.getId(), this);
+        }
         mLowBatteryMap.remove(call);
     }
 
@@ -206,10 +212,108 @@
             return;
         }
 
+        /* There can be chances to miss displaying low battery dialog when user tries to
+         * accept incoming VT upgrade request from HUN due to absence of InCallActivity.
+         * In such cases, show low battery dialog when InCallActivity is visible
+         */
         maybeProcessLowBatteryIndication(call);
     }
 
     /**
+     * This method overrides onCallChanged method of {@interface CallList.CallUpdateListener}
+     * Added for completeness. No implementation yet.
+     */
+     @Override
+     public void onCallChanged(Call call) {
+     }
+
+    /**
+     * This method overrides onLastForwardedNumberChange method of
+     * {@interface CallList.CallUpdateListener}. Added for completeness.
+     *  No implementation yet.
+     */
+     @Override
+     public void onLastForwardedNumberChange() {
+     }
+
+    /**
+     * This method overrides onChildNumberChange method of {@interface CallList.CallUpdateListener}
+     * Added for completeness. No implementation yet.
+     */
+     @Override
+     public void onChildNumberChange() {
+     }
+
+    /**
+     * This method overrides onSessionModificationStateChange method of
+     * {@interface CallList.CallUpdateListener}
+     * @param call The call object for which session modify change occurred
+     * @param sessionModificationState contains the session modification state change
+     */
+     @Override
+     public void onSessionModificationStateChange(Call call, int sessionModificationState) {
+         Log.d(this, "onSessionModificationStateChange call = " + call);
+
+         if (call == null) {
+             return;
+         }
+
+         if (!isIncomingUpgradeReq(sessionModificationState)) {
+             Log.d(this, "onSessionModificationStateChange removing call update listener");
+             CallList.getInstance().removeCallUpdateListener(call.getId(), this);
+             /*
+              * Dismiss low battery dialog (if any) for eg. when incoming upgrade request
+              * times-out possibly because there is no user input on low battery dialog or
+              * when user dismisses the MT upgrade request from HUN with low battery dialog
+              * showing etc.
+              */
+             dismissPendingDialogs();
+         }
+     }
+
+     private boolean isIncomingUpgradeReq(int sessionModificationState) {
+         return (sessionModificationState ==
+                 Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST);
+     }
+
+    /**
+     * When there is user action to modify call as Video call (for eg click on upgrade
+     * button or accepting incoming upgrade request as video), this API checks to see
+     * if UE is under low battery or not and accordingly processes the callType change
+     * to video and returns TRUE if the callType change is handled by this API else FALSE
+     *
+     * @param call The call that is undergoing a change to video call
+     */
+    public boolean onChangeToVideoCall(Call call) {
+        Log.d(this, "onChangeToVideoCall call = " + call);
+        if (call == null || !mPrimaryCallTracker.isPrimaryCall(call)) {
+            return false;
+        }
+
+        if (!isLowBattery(call.getTelecomCall().getDetails())) {
+           // Do not process if the call is changing to Video when UE is NOT under low battery
+           return false;
+        }
+
+        /* If user tries to change to Video call again, then remove the call from
+         * from lowbatterymap to ensure that the dialog will be shown again
+         */
+        if (mLowBatteryMap.replace(call, PROCESS_LOW_BATTERY) == null) {
+            Log.d(this, "this call is not present in the lowbatterymap. add it.");
+            mLowBatteryMap.put(call, PROCESS_LOW_BATTERY);
+        }
+
+        /* Listen to call updates only when there is a incoming upgrade request so that
+           low battery dialog can be dismissed if MT upgrade request times out */
+        if (isIncomingUpgradeReq(call.getSessionModificationState())) {
+            CallList.getInstance().addCallUpdateListener(call.getId(), this);
+        }
+
+        maybeProcessLowBatteryIndication(call);
+        return true;
+    }
+
+    /**
      * When call is answered, this API checks to see if UE is under low battery or not
      * and accordingly processes the low battery video call and returns TRUE if
      * user action to answer the call is handled by this API else FALSE.
@@ -360,10 +464,6 @@
     }
 
     private void maybeProcessLowBatteryIndication(Call call) {
-        if (!isLowBatteryVideoCall(call)) {
-            return;
-        }
-
         if (canProcessLowBatteryIndication(call)) {
             displayLowBatteryAlert(call);
             //mark against the call that the respective low battery indication is processed
@@ -477,6 +577,68 @@
                     }
                 });
             }
+        } else if (isIncomingUpgradeReq(call.getSessionModificationState())) {
+            // Incoming upgrade request handling
+            alertDialog.setPositiveButton(R.string.low_battery_convert, new OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    Log.d(this, "displayLowBatteryAlert decline upgrade request");
+                    CallList.getInstance().removeCallUpdateListener(call.getId(),
+                            InCallLowBatteryListener.this);
+                    InCallPresenter.getInstance().declineUpgradeRequest();
+                }
+            });
+
+            alertDialog.setMessage(R.string.low_battery_msg);
+            alertDialog.setNegativeButton(R.string.low_battery_yes, new OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    Log.d(this, "displayLowBatteryAlert accept upgrade request as Video Call");
+                    /* Control reached here means upgrade req can be accepted only as
+                       bidirectional video call */
+                    VideoProfile videoProfile = new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL);
+                    call.getVideoCall().sendSessionModifyResponse(videoProfile);
+                    call.setSessionModificationState(Call.SessionModificationState.NO_REQUEST);
+                    InCallAudioManager.getInstance().onAcceptUpgradeRequest(call,
+                            VideoProfile.STATE_BIDIRECTIONAL);
+                }
+            });
+        } else {
+            // Outgoing upgrade request handling
+            alertDialog.setPositiveButton(R.string.low_battery_convert, new OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    Log.d(this, "displayLowBatteryAlert change to Voice Call");
+
+                    // Change the audio route to earpiece
+                    InCallAudioManager.getInstance().onModifyCallClicked(call,
+                            VideoProfile.STATE_AUDIO_ONLY);
+                }
+            });
+
+            alertDialog.setMessage(R.string.low_battery_msg);
+            alertDialog.setNegativeButton(R.string.low_battery_yes, new OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    Log.d(this, "displayLowBatteryAlert change to Video Call");
+
+                    VideoCall videoCall = call.getVideoCall();
+                    if (videoCall == null) {
+                        Log.w(this, "displayLowBatteryAlert videocall is null");
+                        return;
+                    }
+
+                    int currUnpausedVideoState = VideoUtils.getUnPausedVideoState(
+                            call.getVideoState());
+                    // Send bidirectional modify request
+                    currUnpausedVideoState |= VideoProfile.STATE_BIDIRECTIONAL;
+
+                    VideoProfile videoProfile = new VideoProfile(currUnpausedVideoState);
+                    videoCall.sendSessionModifyRequest(videoProfile);
+                    call.setSessionModificationState(
+                            Call.SessionModificationState.WAITING_FOR_RESPONSE);
+                }
+            });
         }
 
         mAlert = alertDialog.create();
diff --git a/InCallUI/src/com/android/incallui/InCallMessageController.java b/InCallUI/src/com/android/incallui/InCallMessageController.java
index aa5a57e..d7b7614 100644
--- a/InCallUI/src/com/android/incallui/InCallMessageController.java
+++ b/InCallUI/src/com/android/incallui/InCallMessageController.java
@@ -366,8 +366,14 @@
             return;
         }
 
-        QtiCallUtils.displayToast(mContext,
-                getSessionModificationCauseResourceId(sessionModificationCause));
+        if (sessionModificationCause ==
+                    QtiCallConstants.CAUSE_CODE_SESSION_MODIFY_DOWNGRADE_GENERIC) {
+            QtiCallUtils.displayLongToast(mContext,
+                    getSessionModificationCauseResourceId(sessionModificationCause));
+        } else {
+            QtiCallUtils.displayToast(mContext,
+                    getSessionModificationCauseResourceId(sessionModificationCause));
+        }
     }
 
     /**
@@ -398,6 +404,8 @@
                 return R.string.session_modify_cause_downgrade_thermal_mitigation;
             case QtiCallConstants.CAUSE_CODE_SESSION_MODIFY_DOWNGRADE_LIPSYNC:
                 return R.string.session_modify_cause_downgrade_lipsync;
+            case QtiCallConstants.CAUSE_CODE_SESSION_MODIFY_DOWNGRADE_GENERIC:
+                return R.string.session_modify_cause_downgrade_generic;
             case QtiCallConstants.CAUSE_CODE_SESSION_MODIFY_DOWNGRADE_GENERIC_ERROR:
             default:
                 return R.string.session_modify_cause_downgrade_generic_error;
diff --git a/InCallUI/src/com/android/incallui/InCallOrientationEventListener.java b/InCallUI/src/com/android/incallui/InCallOrientationEventListener.java
index ed10b73..1069189 100644
--- a/InCallUI/src/com/android/incallui/InCallOrientationEventListener.java
+++ b/InCallUI/src/com/android/incallui/InCallOrientationEventListener.java
@@ -108,6 +108,12 @@
             return;
         }
 
+        // Start with current UI orientation.
+        sCurrentOrientation = getCurrentUiOrientation();
+        if (sCurrentOrientation == SCREEN_ORIENTATION_UNKNOWN) {
+            sCurrentOrientation = SCREEN_ORIENTATION_0;
+        }
+
         super.enable();
         mEnabled = true;
         if (notify) {
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index 2a1ecab..fa8ddb5 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -1058,7 +1058,7 @@
         }
 
         Call call = mCallList.getVideoUpgradeRequestCall();
-        if (call != null) {
+        if (call != null && !InCallLowBatteryListener.getInstance().onChangeToVideoCall(call)) {
             VideoProfile videoProfile = new VideoProfile(videoState);
             call.getVideoCall().sendSessionModifyResponse(videoProfile);
             call.setSessionModificationState(Call.SessionModificationState.NO_REQUEST);
diff --git a/InCallUI/src/com/android/incallui/InCallServiceImpl.java b/InCallUI/src/com/android/incallui/InCallServiceImpl.java
index 8f011e3..54bd571 100644
--- a/InCallUI/src/com/android/incallui/InCallServiceImpl.java
+++ b/InCallUI/src/com/android/incallui/InCallServiceImpl.java
@@ -90,6 +90,7 @@
 
         InCallPresenter.getInstance().onServiceUnbind();
         tearDown();
+        StatusBarNotifier.clearAllCallNotifications(getApplicationContext());
 
         return false;
     }
diff --git a/InCallUI/src/com/android/incallui/NotificationBroadcastReceiver.java b/InCallUI/src/com/android/incallui/NotificationBroadcastReceiver.java
index 94a852b..08389ef 100644
--- a/InCallUI/src/com/android/incallui/NotificationBroadcastReceiver.java
+++ b/InCallUI/src/com/android/incallui/NotificationBroadcastReceiver.java
@@ -69,6 +69,7 @@
             InCallPresenter.getInstance().hangUpOngoingCall(context);
         } else if (action.equals(ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST)) {
             InCallPresenter.getInstance().acceptUpgradeRequest(context);
+            InCallPresenter.getInstance().bringToForeground(false);
         } else if (action.equals(ACTION_DECLINE_VIDEO_UPGRADE_REQUEST)) {
             InCallPresenter.getInstance().declineUpgradeRequest(context);
         } else if (action.equals(ACTION_PULL_EXTERNAL_CALL)) {
diff --git a/InCallUI/src/com/android/incallui/QtiCallUtils.java b/InCallUI/src/com/android/incallui/QtiCallUtils.java
index 395fdfe..faf518c 100644
--- a/InCallUI/src/com/android/incallui/QtiCallUtils.java
+++ b/InCallUI/src/com/android/incallui/QtiCallUtils.java
@@ -164,6 +164,20 @@
     }
 
     /**
+     * Displays the message as a long Toast on the UI
+     */
+    public static void displayLongToast(Context context, String msg) {
+        Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
+    }
+
+    /**
+     * Displays the string corresponding to the resourceId as a long Toast on the UI
+     */
+    public static void displayLongToast(Context context, int resourceId) {
+        displayLongToast(context, context.getResources().getString(resourceId));
+    }
+
+    /**
      * The function is called when Modify Call button gets pressed. The function creates and
      * displays modify call options.
      */
diff --git a/src/com/android/dialer/calllog/ContactInfoHelper.java b/src/com/android/dialer/calllog/ContactInfoHelper.java
index ea39d03..c07ca2a 100644
--- a/src/com/android/dialer/calllog/ContactInfoHelper.java
+++ b/src/com/android/dialer/calllog/ContactInfoHelper.java
@@ -292,10 +292,13 @@
                 }
                 String combName = "";
                 for (String num : nums) {
-                    ContactInfo singleCi = lookupContactFromUri(getContactInfoLookupUri(num),
-                            isSip);
+                    ContactInfo singleCi = null;
+                    if (!TextUtils.isEmpty(num)) {
+                        singleCi = lookupContactFromUri(
+                                getContactInfoLookupUri(num), isSip);
+                    }
                     // If contact does not exist, need to avoid changing static empty-contact.
-                    if (singleCi == ContactInfo.EMPTY) {
+                    if (singleCi == null || singleCi == ContactInfo.EMPTY) {
                         singleCi = new ContactInfo();
                     }
                     if (TextUtils.isEmpty(singleCi.name)) {