Backporting references to android.telecom.Call.Details

In L, Call.Details.CAPABILITY_* constants were found in
android.telecom.PhoneCapabilities, it has been verified that the
constant values match as needed.

In L, Call.Details.getExtras had all the information that was moved to
Call.Details.getIntentExtras in M

Added android.telecom.Call.Details compat class

Change-Id: I16d25173382575f351762e0e5b71775f1046fdd0
diff --git a/InCallUI/src/com/android/incallui/Call.java b/InCallUI/src/com/android/incallui/Call.java
index f860c85..0dd7d02 100644
--- a/InCallUI/src/com/android/incallui/Call.java
+++ b/InCallUI/src/com/android/incallui/Call.java
@@ -36,6 +36,7 @@
 import com.android.contacts.common.compat.SdkVersionOverride;
 import com.android.contacts.common.testing.NeededForTesting;
 import com.android.dialer.util.IntentUtil;
+import com.android.incallui.compat.telecom.DetailsCompat;
 import com.android.incallui.util.TelecomCallUtil;
 
 import java.util.ArrayList;
@@ -714,7 +715,7 @@
     }
 
     public Bundle getIntentExtras() {
-        return mTelecomCall == null ? null : mTelecomCall.getDetails().getIntentExtras();
+        return DetailsCompat.getIntentExtras(mTelecomCall.getDetails());
     }
 
     public Bundle getExtras() {
@@ -788,7 +789,7 @@
     }
 
     public boolean hasProperty(int property) {
-        return mTelecomCall.getDetails().hasProperty(property);
+        return DetailsCompat.hasProperty(mTelecomCall.getDetails(), property);
     }
 
     /** Gets the time when the call first became active. */
@@ -797,8 +798,7 @@
     }
 
     public boolean isConferenceCall() {
-        return mTelecomCall.getDetails().hasProperty(
-                android.telecom.Call.Details.PROPERTY_CONFERENCE);
+        return hasProperty(android.telecom.Call.Details.PROPERTY_CONFERENCE);
     }
 
     public GatewayInfo getGatewayInfo() {
@@ -954,8 +954,7 @@
                 "videoState:%s, mSessionModificationState:%d, VideoSettings:%s]",
                 mId,
                 State.toString(getState()),
-                android.telecom.Call.Details
-                        .capabilitiesToString(mTelecomCall.getDetails().getCallCapabilities()),
+                DetailsCompat.capabilitiesToString(mTelecomCall.getDetails().getCallCapabilities()),
                 mChildCallIds,
                 getParentId(),
                 this.mTelecomCall.getConferenceableCalls(),
diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
index 5375b5b..ee45f96 100644
--- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
@@ -28,10 +28,12 @@
 import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_UPGRADE_TO_VIDEO;
 
 import android.content.Context;
+import android.os.Build;
 import android.os.Bundle;
 import android.telecom.InCallService.VideoCall;
 import android.telecom.VideoProfile;
 
+import com.android.contacts.common.compat.SdkVersionOverride;
 import com.android.dialer.compat.CallAudioStateCompat;
 import com.android.incallui.AudioModeProvider.AudioModeListener;
 import com.android.incallui.InCallCameraManager.Listener;
@@ -379,9 +381,7 @@
         final boolean showAddCall = TelecomAdapter.getInstance().canAddCall();
         final boolean showMerge = call.can(
                 android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
-        final boolean showUpgradeToVideo = !isVideo &&
-                (call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX)
-                && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX));
+        final boolean showUpgradeToVideo = !isVideo && hasVideoCallCapabilities(call);
 
         final boolean showMute = call.can(android.telecom.Call.Details.CAPABILITY_MUTE);
 
@@ -400,6 +400,15 @@
         ui.updateButtonStates();
     }
 
+    private boolean hasVideoCallCapabilities(Call call) {
+        if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) {
+            return call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX)
+                    && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX);
+        }
+        // In L, this single flag represents both video transmitting and receiving capabilities
+        return call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX);
+    }
+
     public void refreshMuteState() {
         // Restore the previous mute state
         if (mAutomaticallyMuted &&
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index 170e785..2013a05 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -49,6 +49,7 @@
 import com.android.incallui.InCallPresenter.InCallState;
 import com.android.incallui.InCallPresenter.InCallStateListener;
 import com.android.incallui.InCallPresenter.IncomingCallListener;
+import com.android.incallui.compat.telecom.DetailsCompat;
 import com.android.incalluibind.ObjectFactory;
 
 import java.lang.ref.WeakReference;
@@ -1056,10 +1057,13 @@
      * @return {@code true} if the toast should be shown, {@code false} otherwise.
      */
     private boolean shouldShowNoteSentToast(Call call) {
-        return call != null && !TextUtils
-                .isEmpty(call.getTelecomCall().getDetails().getIntentExtras().getString(
-                        TelecomManager.EXTRA_CALL_SUBJECT)) &&
-                (call.getState() == Call.State.DIALING || call.getState() == Call.State.CONNECTING);
+        return call != null && hasCallSubject(call) && (call.getState() == Call.State.DIALING
+                || call.getState() == Call.State.CONNECTING);
+    }
+
+    private static boolean hasCallSubject(Call call) {
+        return !TextUtils.isEmpty(DetailsCompat.getIntentExtras(
+                call.getTelecomCall().getDetails()).getString(TelecomManager.EXTRA_CALL_SUBJECT));
     }
 
     public interface CallCardUi extends Ui {
diff --git a/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java b/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java
index ad8de74..79c5fae 100644
--- a/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java
+++ b/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java
@@ -37,6 +37,7 @@
 import com.android.contacts.common.preference.ContactsPreferences;
 import com.android.contacts.common.util.ContactDisplayUtils;
 import com.android.incallui.ContactInfoCache.ContactCacheEntry;
+import com.android.incallui.compat.telecom.DetailsCompat;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -352,9 +353,10 @@
                     new ContactLookupCallback(this));
         }
 
-        boolean thisRowCanSeparate = mParentCanSeparate && call.getTelecomCall().getDetails().can(
+        boolean thisRowCanSeparate = mParentCanSeparate && DetailsCompat.can(
+                call.getTelecomCall().getDetails(),
                 android.telecom.Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE);
-        boolean thisRowCanDisconnect = call.getTelecomCall().getDetails().can(
+        boolean thisRowCanDisconnect = DetailsCompat.can(call.getTelecomCall().getDetails(),
                 android.telecom.Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE);
 
         setCallerInfoForRow(result, contactCache.namePrimary,
diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java
index db032e2..dff2747 100644
--- a/InCallUI/src/com/android/incallui/InCallActivity.java
+++ b/InCallUI/src/com/android/incallui/InCallActivity.java
@@ -58,6 +58,7 @@
 import com.android.dialer.logging.Logger;
 import com.android.dialer.logging.ScreenEvent;
 import com.android.incallui.Call.State;
+import com.android.incallui.compat.telecom.DetailsCompat;
 import com.android.incallui.util.AccessibilityUtil;
 import com.android.phone.common.animation.AnimUtils;
 import com.android.phone.common.animation.AnimationListenerAdapter;
@@ -572,7 +573,7 @@
 
                 Bundle extras = null;
                 if (call != null) {
-                    extras = call.getTelecomCall().getDetails().getIntentExtras();
+                    extras = DetailsCompat.getIntentExtras(call.getTelecomCall().getDetails());
                 }
                 if (extras == null) {
                     // Initialize the extras bundle to avoid NPE
@@ -609,8 +610,8 @@
             Call pendingAccountSelectionCall = CallList.getInstance().getWaitingForAccountCall();
             if (pendingAccountSelectionCall != null) {
                 showCallCardFragment(false);
-                Bundle extras = pendingAccountSelectionCall
-                        .getTelecomCall().getDetails().getIntentExtras();
+                Bundle extras = DetailsCompat.getIntentExtras(pendingAccountSelectionCall
+                        .getTelecomCall().getDetails());
 
                 final List<PhoneAccountHandle> phoneAccountHandles;
                 if (extras != null) {
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index 697bead..637b4aa 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -53,6 +53,7 @@
 import com.android.dialer.filterednumber.FilteredNumbersUtil;
 import com.android.dialer.logging.InteractionEvent;
 import com.android.dialer.logging.Logger;
+import com.android.incallui.compat.telecom.DetailsCompat;
 import com.android.incallui.util.TelecomCallUtil;
 import com.android.incalluibind.ObjectFactory;
 import com.google.common.base.Preconditions;
@@ -1445,7 +1446,7 @@
     private void setDisconnectCauseForMissingAccounts(Call call) {
         android.telecom.Call telecomCall = call.getTelecomCall();
 
-        Bundle extras = telecomCall.getDetails().getIntentExtras();
+        Bundle extras = DetailsCompat.getIntentExtras(telecomCall.getDetails());
         // Initialize the extras bundle to avoid NPE
         if (extras == null) {
             extras = new Bundle();
diff --git a/InCallUI/src/com/android/incallui/TelecomAdapter.java b/InCallUI/src/com/android/incallui/TelecomAdapter.java
index 45daeed..99fcbed 100644
--- a/InCallUI/src/com/android/incallui/TelecomAdapter.java
+++ b/InCallUI/src/com/android/incallui/TelecomAdapter.java
@@ -24,6 +24,8 @@
 
 import com.google.common.base.Preconditions;
 
+import com.android.incallui.compat.telecom.DetailsCompat;
+
 import java.util.List;
 
 final class TelecomAdapter implements InCallServiceListener {
@@ -135,7 +137,7 @@
             if (!conferenceable.isEmpty()) {
                 call.conference(conferenceable.get(0));
             } else {
-                if (call.getDetails().can(
+                if (DetailsCompat.can(call.getDetails(),
                         android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE)) {
                     call.mergeConference();
                 }
@@ -148,7 +150,7 @@
     void swap(String callId) {
         android.telecom.Call call = getTelecomCallById(callId);
         if (call != null) {
-            if (call.getDetails().can(
+            if (DetailsCompat.can(call.getDetails(),
                     android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE)) {
                 call.swapConference();
             }
diff --git a/InCallUI/src/com/android/incallui/compat/telecom/DetailsCompat.java b/InCallUI/src/com/android/incallui/compat/telecom/DetailsCompat.java
new file mode 100644
index 0000000..fe24340
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/compat/telecom/DetailsCompat.java
@@ -0,0 +1,87 @@
+package com.android.incallui.compat.telecom;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.telecom.Call.Details;
+
+import com.android.contacts.common.compat.SdkVersionOverride;
+import com.android.incallui.Log;
+
+/**
+ * Compatibility class for {@link Details}
+ */
+public class DetailsCompat {
+
+    /**
+     * Returns the intent extras from the given {@link Details}
+     * For Sdk version L and earlier, this will return {@link Details#getExtras()}
+     *
+     * @param details The details whose intent extras should be returned
+     * @return The given details' intent extras
+     */
+    public static Bundle getIntentExtras(Details details) {
+        if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) {
+            return details.getIntentExtras();
+        }
+        return details.getExtras();
+    }
+
+    /**
+     * Compatibility method to check whether the supplied properties includes the
+     * specified property.
+     *
+     * @param details The details whose properties should be checked.
+     * @param property The property to check properties for.
+     * @return Whether the specified property is supported.
+     */
+    public static boolean hasProperty(Details details, int property) {
+        if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) {
+            return details.hasProperty(property);
+        }
+        return (details.getCallProperties() & property) != 0;
+    }
+
+    /**
+     * Compatibility method to check whether the capabilities of the given {@code Details}
+     * supports the specified capability.
+     *
+     * @param details The details whose capabilities should be checked.
+     * @param capability The capability to check capabilities for.
+     * @return Whether the specified capability is supported.
+     */
+    public static boolean can(Details details, int capability) {
+        if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) {
+            return details.can(capability);
+        }
+        return (details.getCallCapabilities() & capability) != 0;
+    }
+
+    /**
+     * Render a set of capability bits ({@code CAPABILITY_*}) as a human readable string.
+     *
+     * @param capabilities A capability bit field.
+     * @return A human readable string representation.
+     */
+    public static String capabilitiesToString(int capabilities) {
+        if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) {
+            return Details.capabilitiesToString(capabilities);
+        }
+        return capabilitiesToStringLollipop(capabilities);
+    }
+
+    /*
+     * Use reflection to call PhoneCapabilities.toString. InCallUI code is only run on Google
+     * Experience phones, so we will be the system Dialer and the method will exist
+     */
+    private static String capabilitiesToStringLollipop(int capabilities) {
+        try {
+            return (String) Class.forName("android.telecom.PhoneCapabilities")
+                    .getMethod("toString", Integer.TYPE)
+                    .invoke(null, capabilities);
+        } catch (ReflectiveOperationException e) {
+            Log.e(DetailsCompat.class, "Unable to use reflection to call "
+                    + "android.telecom.PhoneCapabilities.toString(int)", e);
+            return String.valueOf(capabilities);
+        }
+    }
+}