Merge "Add location information with business caller ID." into ub-contactsdialer-a-dev
diff --git a/InCallUI/res/drawable-hdpi/ic_business_white_24dp.png b/InCallUI/res/drawable-hdpi/ic_business_white_24dp.png
new file mode 100644
index 0000000..d10ebb7
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/ic_business_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_location_on_white_24dp.png b/InCallUI/res/drawable-hdpi/ic_location_on_white_24dp.png
new file mode 100644
index 0000000..7c281c3
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/ic_location_on_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_business_white_24dp.png b/InCallUI/res/drawable-mdpi/ic_business_white_24dp.png
new file mode 100644
index 0000000..7b9227c
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/ic_business_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_location_on_white_24dp.png b/InCallUI/res/drawable-mdpi/ic_location_on_white_24dp.png
new file mode 100644
index 0000000..933eb51
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/ic_location_on_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_business_white_24dp.png b/InCallUI/res/drawable-xhdpi/ic_business_white_24dp.png
new file mode 100644
index 0000000..e563045
--- /dev/null
+++ b/InCallUI/res/drawable-xhdpi/ic_business_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_location_on_white_24dp.png b/InCallUI/res/drawable-xhdpi/ic_location_on_white_24dp.png
new file mode 100644
index 0000000..814ca8d
--- /dev/null
+++ b/InCallUI/res/drawable-xhdpi/ic_location_on_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_business_white_24dp.png b/InCallUI/res/drawable-xxhdpi/ic_business_white_24dp.png
new file mode 100644
index 0000000..7dfc8dc
--- /dev/null
+++ b/InCallUI/res/drawable-xxhdpi/ic_business_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_location_on_white_24dp.png b/InCallUI/res/drawable-xxhdpi/ic_location_on_white_24dp.png
new file mode 100644
index 0000000..078b10d
--- /dev/null
+++ b/InCallUI/res/drawable-xxhdpi/ic_location_on_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_business_white_24dp.png b/InCallUI/res/drawable-xxxhdpi/ic_business_white_24dp.png
new file mode 100644
index 0000000..c9aea72
--- /dev/null
+++ b/InCallUI/res/drawable-xxxhdpi/ic_business_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_location_on_white_24dp.png b/InCallUI/res/drawable-xxxhdpi/ic_location_on_white_24dp.png
new file mode 100644
index 0000000..8bcb6f6
--- /dev/null
+++ b/InCallUI/res/drawable-xxxhdpi/ic_location_on_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/layout/business_context_info_list_item.xml b/InCallUI/res/layout/business_context_info_list_item.xml
index 7ed528c..eba98ac 100644
--- a/InCallUI/res/layout/business_context_info_list_item.xml
+++ b/InCallUI/res/layout/business_context_info_list_item.xml
@@ -15,11 +15,14 @@
 -->
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
-     android:layout_height="wrap_content">
+     android:layout_height="wrap_content"
+     android:paddingEnd="@dimen/business_contact_context_end_padding">
      <ImageView android:id="@+id/icon"
          android:layout_width="@dimen/business_contact_context_image_size"
          android:layout_height="@dimen/business_contact_context_image_size"
          android:scaleType="centerCrop"
+         android:tint="@color/business_contact_context_text_color"
+         android:alpha="0.8"
          android:layout_centerVertical="true"
          android:layout_marginLeft="@dimen/business_contact_context_image_padding"
          android:layout_marginRight="@dimen/business_contact_context_image_padding"/>
diff --git a/InCallUI/res/values/dimens.xml b/InCallUI/res/values/dimens.xml
index 02527d3..e407846 100644
--- a/InCallUI/res/values/dimens.xml
+++ b/InCallUI/res/values/dimens.xml
@@ -133,6 +133,7 @@
     <dimen name="person_contact_context_detail_padding_top">7dp</dimen>
     <dimen name="person_contact_context_detail_text_size">14sp</dimen>
 
+    <dimen name="business_contact_context_end_padding">10dp</dimen>
     <dimen name="business_contact_context_image_size">25dp</dimen>
     <dimen name="business_contact_context_image_padding">20dp</dimen>
     <dimen name="business_contact_context_heading_font_size">18sp</dimen>
diff --git a/InCallUI/res/values/strings.xml b/InCallUI/res/values/strings.xml
index 0d0aab3..7a90953 100644
--- a/InCallUI/res/values/strings.xml
+++ b/InCallUI/res/values/strings.xml
@@ -470,4 +470,11 @@
 
     <!-- Title for the call context with a business-type contact. [CHAR LIMIT=40] -->
     <string name="business_contact_context_title">Business info</string>
+
+    <!-- Distance strings for business caller ID context. -->
+
+    <!-- Used to inform the user how far away a location is in miles. [CHAR LIMIT=NONE] -->
+    <string name="distance_imperial_away"><xliff:g id="distance">%.1f</xliff:g> mi away</string>
+    <!-- Used to inform the user how far away a location is in kilometers. [CHAR LIMIT=NONE] -->
+    <string name="distance_metric_away"><xliff:g id="distance">%.1f</xliff:g> km away</string>
 </resources>
diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java
index ffc7cb6..b276a95 100644
--- a/InCallUI/src/com/android/incallui/CallCardFragment.java
+++ b/InCallUI/src/com/android/incallui/CallCardFragment.java
@@ -46,15 +46,13 @@
 import android.view.animation.AnimationUtils;
 import android.widget.ImageButton;
 import android.widget.ImageView;
+import android.widget.ListAdapter;
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.Toast;
 
 import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
 import com.android.contacts.common.widget.FloatingActionButtonController;
-import com.android.incallui.InCallContactInteractions.BusinessContextInfo;
-import com.android.incallui.InCallContactInteractions.PersonContextInfo;
-import com.android.incallui.InCallContactInteractions.ContactContextInfo;
 import com.android.phone.common.animation.AnimUtils;
 
 import java.util.List;
@@ -336,23 +334,18 @@
     }
 
     @Override
-    public void setContactContext(boolean isBusiness, List<ContactContextInfo> info) {
-        mPhoto.setVisibility(View.GONE);
-        mPrimaryCallCardContainer.setElevation(0);
-        mContactContext.setVisibility(View.VISIBLE);
-
-        if (mInCallContactInteractions == null) {
-            mInCallContactInteractions =
-                    new InCallContactInteractions(getView().getContext(), isBusiness);
-        } else {
-            mInCallContactInteractions.setIsBusiness(isBusiness);
-        }
-
-        mContactContextTitle.setText(mInCallContactInteractions.getContactContextTitle());
-        mInCallContactInteractions.setData(info);
-        mContactContextListView.setAdapter(mInCallContactInteractions.getListAdapter());
+    public void setContactContext(String title, ListAdapter listAdapter) {
+        mContactContextTitle.setText(title);
+        mContactContextListView.setAdapter(listAdapter);
     }
 
+    @Override
+    public void showContactContext(boolean show) {
+            mPhoto.setVisibility(show ? View.GONE : View.VISIBLE);
+            mPrimaryCallCardContainer.setElevation(
+                    show ? 0 : getResources().getDimension(R.dimen.primary_call_elevation));
+            mContactContext.setVisibility(show ? View.VISIBLE : View.GONE);
+    }
 
     /**
      * Sets the visibility of the primary call card.
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index ae1a48a..9c47a93 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -36,8 +36,10 @@
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 import android.view.accessibility.AccessibilityManager;
+import android.widget.ListAdapter;
 
 import com.android.incallui.Call.LogState;
+import com.android.incallui.Call.State;
 import com.android.incallui.InCallContactInteractions.ContactContextInfo;
 import com.android.incallui.ContactInfoCache.ContactCacheEntry;
 import com.android.incallui.ContactInfoCache.ContactInfoCacheCallback;
@@ -48,6 +50,7 @@
 import com.android.incallui.InCallPresenter.IncomingCallListener;
 import com.android.incalluibind.ObjectFactory;
 
+import java.io.IOException;
 import java.lang.ref.WeakReference;
 import java.util.List;
 
@@ -60,7 +63,7 @@
  */
 public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
         implements InCallStateListener, IncomingCallListener, InCallDetailsListener,
-        InCallEventListener, CallList.CallUpdateListener {
+        InCallEventListener, CallList.CallUpdateListener, DistanceHelper.Listener {
 
     public interface EmergencyCallListener {
         public void onCallUpdated(BaseFragment fragment, boolean isEmergency);
@@ -71,6 +74,7 @@
 
     private final EmergencyCallListener mEmergencyCallListener =
             ObjectFactory.newEmergencyCallListener();
+    private DistanceHelper mDistanceHelper;
 
     private Call mPrimary;
     private Call mSecondary;
@@ -80,6 +84,7 @@
     private Context mContext;
     private boolean mSpinnerShowing = false;
     private boolean mHasShownToast = false;
+    private InCallContactInteractions mInCallContactInteractions;
 
     public static class ContactLookupCallback implements ContactInfoCacheCallback {
         private final WeakReference<CallCardPresenter> mCallCardPresenter;
@@ -106,6 +111,13 @@
             }
         }
 
+        @Override
+        public void onContactInteractionsInfoComplete(String callId, ContactCacheEntry entry) {
+            CallCardPresenter presenter = mCallCardPresenter.get();
+            if (presenter != null) {
+                presenter.onContactInteractionsInfoComplete(callId, entry);
+            }
+        }
     }
 
     public CallCardPresenter() {
@@ -120,6 +132,7 @@
 
     public void init(Context context, Call call) {
         mContext = Preconditions.checkNotNull(context);
+        mDistanceHelper = ObjectFactory.newDistanceHelper(mContext, this);
 
         // Call may be null if disconnect happened already.
         if (call != null) {
@@ -172,6 +185,10 @@
             CallList.getInstance().removeCallUpdateListener(mPrimary.getId(), this);
         }
 
+        if (mDistanceHelper != null) {
+            mDistanceHelper.cleanUp();
+        }
+
         mPrimary = null;
         mPrimaryContactInfo = null;
         mSecondaryContactInfo = null;
@@ -210,6 +227,10 @@
             secondary = getCallToDisplay(callList, primary, true);
         }
 
+        if (mInCallContactInteractions != null) {
+            ui.showContactContext(newState != InCallState.INCOMING);
+        }
+
         Log.d(this, "Primary call: " + primary);
         Log.d(this, "Secondary call: " + secondary);
 
@@ -551,6 +572,54 @@
         }
     }
 
+    private void onContactInteractionsInfoComplete(String callId, ContactCacheEntry entry) {
+        if (getUi() == null) {
+            return;
+        }
+
+        if (mPrimary != null && callId.equals(mPrimary.getId())) {
+            mPrimaryContactInfo.locationAddress = entry.locationAddress;
+            updateContactInteractions();
+        }
+    }
+
+    @Override
+    public void onLocationReady() {
+        // This will only update the contacts interactions data if the location returns after
+        // the contact information is found.
+        updateContactInteractions();
+    }
+
+    private void updateContactInteractions() {
+        if (mPrimary != null && mPrimaryContactInfo != null
+                && mPrimaryContactInfo.locationAddress != null) {
+            setInCallContactInteractionsType(true);
+
+            mInCallContactInteractions.setBusinessInfo(
+                    mPrimaryContactInfo.locationAddress,
+                    mDistanceHelper.calculateDistance(mPrimaryContactInfo.locationAddress));
+            getUi().setContactContext(
+                    null,
+                    mInCallContactInteractions.getListAdapter());
+            getUi().showContactContext(mPrimary.getState() != State.INCOMING);
+        }
+    }
+
+    /**
+     * Update the contact interactions type so that the correct UI is shown.
+     *
+     * @param isBusiness {@code true} if the interaction is a business interaction, {@code false} if
+     * it is a personal contact.
+     */
+    private void setInCallContactInteractionsType(boolean isBusiness) {
+        if (mInCallContactInteractions == null) {
+            mInCallContactInteractions =
+                    new InCallContactInteractions(mContext, isBusiness);
+        } else {
+            mInCallContactInteractions.setIsBusiness(isBusiness);
+        }
+    }
+
     private void updateContactEntry(ContactCacheEntry entry, boolean isPrimary) {
         if (isPrimary) {
             mPrimaryContactInfo = entry;
@@ -962,7 +1031,8 @@
 
     public interface CallCardUi extends Ui {
         void setVisible(boolean on);
-        void setContactContext(boolean isBusiness, List<ContactContextInfo> info);
+        void setContactContext(String title, ListAdapter listAdapter);
+        void showContactContext(boolean show);
         void setCallCardVisible(boolean visible);
         void setPrimary(String number, String name, boolean nameIsNumber, String label,
                 Drawable photo, boolean isSipCall, boolean isContactPhotoShown);
diff --git a/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java b/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java
index 0e6d6e9..a0588a1 100644
--- a/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java
+++ b/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java
@@ -137,6 +137,9 @@
             update(callId, entry);
         }
 
+        @Override
+        public void onContactInteractionsInfoComplete(String callId, ContactCacheEntry entry) {}
+
         /**
          * Updates the contact information for a participant.
          *
diff --git a/InCallUI/src/com/android/incallui/ContactInfoCache.java b/InCallUI/src/com/android/incallui/ContactInfoCache.java
index 7de1648..0e6a3d4 100644
--- a/InCallUI/src/com/android/incallui/ContactInfoCache.java
+++ b/InCallUI/src/com/android/incallui/ContactInfoCache.java
@@ -20,6 +20,7 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.location.Address;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Looper;
@@ -44,7 +45,7 @@
 
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
 import com.google.common.base.Preconditions;
 
 import java.util.HashMap;
@@ -71,6 +72,7 @@
 
     private Drawable mDefaultContactPhotoDrawable;
     private Drawable mConferencePhotoDrawable;
+    private ContactUtils mContactUtils;
 
     public static synchronized ContactInfoCache getInstance(Context mContext) {
         if (sCache == null) {
@@ -84,6 +86,8 @@
         mPhoneNumberService = ObjectFactory.newPhoneNumberService(context);
         mCachedNumberLookupService =
                 com.android.dialerbind.ObjectFactory.newCachedNumberLookupService();
+        mContactUtils = ObjectFactory.getContactUtilsInstance(context);
+
     }
 
     public ContactCacheEntry getInfo(String callId) {
@@ -249,7 +253,7 @@
     }
 
     class PhoneNumberServiceListener implements PhoneNumberService.NumberLookupListener,
-                                     PhoneNumberService.ImageLookupListener {
+                                     PhoneNumberService.ImageLookupListener, ContactUtils.Listener {
         private final String mCallId;
 
         PhoneNumberServiceListener(String callId) {
@@ -295,12 +299,18 @@
                 entry.photo = mContext.getResources().getDrawable(R.drawable.img_business);
             }
 
+            String address = null;
+            if (mContactUtils != null) {
+                // This method will callback "onAddressDetailsFound".
+                address = mContactUtils.getAddressFromLookupKey(info.getLookupKey(), this);
+            }
+
             // Add the contact info to the cache.
             mInfoMap.put(mCallId, entry);
             sendInfoNotifications(mCallId, entry);
 
             // If there is no image then we should not expect another callback.
-            if (info.getImageUrl() == null) {
+            if (info.getImageUrl() == null && address == null) {
                 // We're done, so clear callbacks
                 clearCallbacks(mCallId);
             }
@@ -310,6 +320,14 @@
         public void onImageFetchComplete(Bitmap bitmap) {
             onImageLoadComplete(TOKEN_UPDATE_PHOTO_FOR_CALL_STATE, null, bitmap, mCallId);
         }
+
+        @Override
+        public void onAddressDetailsFound(Address address) {
+            final ContactCacheEntry entry = mInfoMap.get(mCallId);
+            entry.locationAddress = address;
+            sendContactInteractionsNotifications(mCallId, entry);
+            clearCallbacks(mCallId);
+        }
     }
 
     /**
@@ -531,6 +549,15 @@
         }
     }
 
+    private void sendContactInteractionsNotifications(String callId, ContactCacheEntry entry) {
+        final Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId);
+        if (callBacks != null) {
+            for (ContactInfoCacheCallback callBack : callBacks) {
+                callBack.onContactInteractionsInfoComplete(callId, entry);
+            }
+        }
+    }
+
     private void clearCallbacks(String callId) {
         mCallBacks.remove(callId);
     }
@@ -570,6 +597,7 @@
     public interface ContactInfoCacheCallback {
         public void onContactInfoComplete(String callId, ContactCacheEntry entry);
         public void onImageLoadComplete(String callId, ContactCacheEntry entry);
+        public void onContactInteractionsInfoComplete(String callId, ContactCacheEntry entry);
     }
 
     public static class ContactCacheEntry {
@@ -585,11 +613,12 @@
         public Uri displayPhotoUri;
         public Uri lookupUri; // Sent to NotificationMananger
         public String lookupKey;
+        public Address locationAddress;
         public int contactLookupResult = LogState.LOOKUP_NOT_FOUND;
 
         @Override
         public String toString() {
-            return Objects.toStringHelper(this)
+            return MoreObjects.toStringHelper(this)
                     .add("name", MoreStrings.toSafeString(name))
                     .add("number", MoreStrings.toSafeString(number))
                     .add("location", MoreStrings.toSafeString(location))
@@ -598,6 +627,7 @@
                     .add("isSipCall", isSipCall)
                     .add("contactUri", contactUri)
                     .add("displayPhotoUri", displayPhotoUri)
+                    .add("locationAddress", locationAddress)
                     .add("contactLookupResult", contactLookupResult)
                     .toString();
         }
diff --git a/InCallUI/src/com/android/incallui/ContactUtils.java b/InCallUI/src/com/android/incallui/ContactUtils.java
new file mode 100644
index 0000000..eac7484
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/ContactUtils.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.incallui;
+
+import android.content.Context;
+import android.location.Address;
+
+import com.android.incalluibind.ObjectFactory;
+
+/**
+ * Utility functions to help manipulate contact data.
+ */
+public abstract class ContactUtils {
+    protected Context mContext;
+
+    public static ContactUtils getInstance(Context context) {
+        return ObjectFactory.getContactUtilsInstance(context);
+    }
+
+    protected ContactUtils(Context context) {
+        mContext = context;
+    }
+
+    public interface Listener {
+        public void onAddressDetailsFound(Address address);
+    }
+
+    public abstract String getAddressFromLookupKey(String lookupKey, Listener listener);
+}
diff --git a/InCallUI/src/com/android/incallui/DistanceHelper.java b/InCallUI/src/com/android/incallui/DistanceHelper.java
new file mode 100644
index 0000000..a4db5fe
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/DistanceHelper.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.incallui;
+
+import android.location.Address;
+
+/**
+ * Superclass for a helper class to get the current location and distance to other locations.
+ */
+public abstract class DistanceHelper {
+    public static final float DISTANCE_NOT_FOUND = -1;
+    public static final float MILES_PER_METER = (float) 0.000621371192;
+    public static final float KILOMETERS_PER_METER = (float) 0.001;
+
+    public interface Listener {
+        public void onLocationReady();
+    }
+
+    public void cleanUp() {}
+
+    public float calculateDistance(Address address) {
+        return DISTANCE_NOT_FOUND;
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/InCallContactInteractions.java b/InCallUI/src/com/android/incallui/InCallContactInteractions.java
index 0ed8b20..6f30720 100644
--- a/InCallUI/src/com/android/incallui/InCallContactInteractions.java
+++ b/InCallUI/src/com/android/incallui/InCallContactInteractions.java
@@ -17,6 +17,8 @@
 package com.android.incallui;
 
 import android.content.Context;
+import android.location.Address;
+import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -27,7 +29,9 @@
 import android.widget.RelativeLayout.LayoutParams;
 import android.widget.TextView;
 
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * Wrapper class for objects that are used in generating the context about the contact in the InCall
@@ -64,13 +68,40 @@
         }
     }
 
-    /**
-     * Set the data for the list adapter.
-     * @param data The data to add to the list adapter. This completely replaces any previous data.
-     */
-    public void setData(List<ContactContextInfo> data) {
+    public void setBusinessInfo(Address address, float distance) {
         mListAdapter.clear();
-        mListAdapter.addAll(data);
+        mListAdapter.addAll(constructBusinessContextInfo(address, distance));
+    }
+
+    private List<ContactContextInfo> constructBusinessContextInfo(Address address, float distance) {
+        List<ContactContextInfo> info = new ArrayList<ContactContextInfo>();
+
+        BusinessContextInfo headerInfo = new BusinessContextInfo();
+        headerInfo.iconId = R.drawable.ic_business_white_24dp;
+        headerInfo.heading = getContactContextTitle();
+        info.add(headerInfo);
+
+        //TODO: hours of operation information
+
+        // Location information
+        BusinessContextInfo distanceInfo = new BusinessContextInfo();
+        distanceInfo.iconId = R.drawable.ic_location_on_white_24dp;
+        if (distance != DistanceHelper.DISTANCE_NOT_FOUND) {
+            //TODO: add a setting to allow the user to select "KM" or "MI" as their distance units.
+            if (Locale.US.equals(Locale.getDefault())) {
+                distanceInfo.heading = mContext.getString(R.string.distance_imperial_away,
+                        distance * DistanceHelper.MILES_PER_METER);
+            } else {
+                distanceInfo.heading = mContext.getString(R.string.distance_metric_away,
+                        distance * DistanceHelper.KILOMETERS_PER_METER);
+            }
+        }
+        if (address != null) {
+            distanceInfo.detail = address.getAddressLine(0);
+        }
+        info.add(distanceInfo);
+
+        return info;
     }
 
     /**
@@ -99,13 +130,20 @@
             TextView headingTextView = (TextView) listItem.findViewById(R.id.heading);
             TextView detailTextView = (TextView) listItem.findViewById(R.id.detail);
 
-            if (this.iconId == 0 || this.heading == null || this.detail == null) {
+            if (this.iconId == 0 || (this.heading == null && this.detail == null)) {
                 return;
             }
 
             imageView.setImageDrawable(listItem.getContext().getDrawable(this.iconId));
+
             headingTextView.setText(this.heading);
+            headingTextView.setVisibility(TextUtils.isEmpty(this.heading)
+                    ? View.GONE : View.VISIBLE);
+
             detailTextView.setText(this.detail);
+            detailTextView.setVisibility(TextUtils.isEmpty(this.detail)
+                    ? View.GONE : View.VISIBLE);
+
         }
     }
 
@@ -157,9 +195,7 @@
             LayoutInflater inflater = (LayoutInflater)
                     getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
-            View listItem;
-            listItem = inflater.inflate(mResId, null);
-
+            View listItem = inflater.inflate(mResId, null);
             ContactContextInfo item = getItem(position);
 
             if (item == null) {
@@ -167,7 +203,6 @@
             }
 
             item.bindView(listItem);
-
             return listItem;
         }
     }
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
index 30114b8..d1509b8 100644
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -188,6 +188,9 @@
                     buildAndSendNotification(call, entry);
                 }
             }
+
+            @Override
+            public void onContactInteractionsInfoComplete(String callId, ContactCacheEntry entry) {}
         });
     }
 
diff --git a/InCallUI/src/com/android/incallui/service/PhoneNumberService.java b/InCallUI/src/com/android/incallui/service/PhoneNumberService.java
index cddc478..70da4ef 100644
--- a/InCallUI/src/com/android/incallui/service/PhoneNumberService.java
+++ b/InCallUI/src/com/android/incallui/service/PhoneNumberService.java
@@ -60,6 +60,7 @@
         public String getPhoneLabel();
         public String getNormalizedNumber();
         public String getImageUrl();
+        public String getLookupKey();
         public boolean isBusiness();
         public int getLookupSource();
     }
diff --git a/InCallUI/src/com/android/incalluibind/ObjectFactory.java b/InCallUI/src/com/android/incalluibind/ObjectFactory.java
index d8a2e29..7e9423a 100644
--- a/InCallUI/src/com/android/incalluibind/ObjectFactory.java
+++ b/InCallUI/src/com/android/incalluibind/ObjectFactory.java
@@ -20,6 +20,8 @@
 import android.content.Intent;
 
 import com.android.incallui.CallCardPresenter.EmergencyCallListener;
+import com.android.incallui.ContactUtils;
+import com.android.incallui.DistanceHelper;
 import com.android.incallui.service.PhoneNumberService;
 
 public class ObjectFactory {
@@ -45,4 +47,13 @@
     public static Intent getCallStateButtonBroadcastIntent(Context context) {
         return null;
     }
+
+    public static DistanceHelper newDistanceHelper(Context context,
+            DistanceHelper.Listener listener) {
+        return null;
+    }
+
+    public static ContactUtils getContactUtilsInstance(Context context) {
+        return null;
+    }
 }