Merge "Update dialer translation strings."
diff --git a/Android.mk b/Android.mk
index 5eb098d..beaa2c5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -89,11 +89,6 @@
 
 LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(RES_DIRS))
 
-define all-dialer-manifest-extra-packages
-  $(shell cd $(LOCAL_PATH) ; \
-          find . -type f -name "AndroidManifest.xml" | uniq | sort | cut -c 8- | rev | cut -c 21- | rev | sed 's/\//./g')
-endef
-
 EXCLUDE_EXTRA_PACKAGES := \
 	com.android.dialer.binary.aosp.testing \
 	com.android.dialer.binary.google \
@@ -101,7 +96,80 @@
 	com.android.incallui.maps.impl \
 
 # We specify each package explicitly to glob resource files.
-LOCAL_AAPT_FLAGS := $(call all-dialer-manifest-extra-packages)
+# find . -type f -name "AndroidManifest.xml" | uniq | sort | cut -c 8- | rev | cut -c 21- | rev | sed 's/\//./g' | sed 's/$/ \\/'
+LOCAL_AAPT_FLAGS := \
+	com.android.contacts.common \
+	com.android.dialer.about \
+	com.android.dialer.app \
+	com.android.dialer.app.manifests.activities \
+	com.android.dialer.app.voicemail.error \
+	com.android.dialer.backup \
+	com.android.dialer.binary.aosp.testing \
+	com.android.dialer.binary.google \
+	com.android.dialer.blocking \
+	com.android.dialer.callcomposer \
+	com.android.dialer.callcomposer.camera \
+	com.android.dialer.callcomposer.camera.camerafocus \
+	com.android.dialer.callcomposer.cameraui \
+	com.android.dialer.calldetails \
+	com.android.dialer.calllog.database \
+	com.android.dialer.calllog.ui \
+	com.android.dialer.calllogutils \
+	com.android.dialer.common \
+	com.android.dialer.configprovider \
+	com.android.dialer.contactactions \
+	com.android.dialer.contactphoto \
+	com.android.dialer.contactsfragment \
+	com.android.dialer.databasepopulator \
+	com.android.dialer.dialpadview \
+	com.android.dialer.enrichedcall.simulator \
+	com.android.dialer.interactions \
+	com.android.dialer.lettertile \
+	com.android.dialer.location \
+	com.android.dialer.main.impl \
+	com.android.dialer.notification \
+	com.android.dialer.oem \
+	com.android.dialer.phonenumberutil \
+	com.android.dialer.postcall \
+	com.android.dialer.searchfragment.common \
+	com.android.dialer.searchfragment.cp2 \
+	com.android.dialer.searchfragment.list \
+	com.android.dialer.searchfragment.nearbyplaces \
+	com.android.dialer.searchfragment.remote \
+	com.android.dialershared.bubble \
+	com.android.dialer.shortcuts \
+	com.android.dialer.simulator.impl \
+	com.android.dialer.speeddial \
+	com.android.dialer.theme \
+	com.android.dialer.util \
+	com.android.dialer.voicemail.listui \
+	com.android.dialer.voicemailstatus \
+	com.android.dialer.widget \
+	com.android.incallui \
+	com.android.incallui.answer.impl.affordance \
+	com.android.incallui.answer.impl \
+	com.android.incallui.answer.impl.answermethod \
+	com.android.incallui.answer.impl.hint \
+	com.android.incallui.audioroute \
+	com.android.incallui.autoresizetext \
+	com.android.incallui.calllocation.impl \
+	com.android.incallui.callpending \
+	com.android.incallui.commontheme \
+	com.android.incallui.contactgrid \
+	com.android.incallui.disconnectdialog \
+	com.android.incallui.hold \
+	com.android.incallui.incall.impl \
+	com.android.incallui.maps.impl \
+	com.android.incallui.sessiondata \
+	com.android.incallui.spam \
+	com.android.incallui.speakerbuttonlogic \
+	com.android.incallui.telecomeventui \
+	com.android.incallui.video.impl \
+	com.android.incallui.video.protocol \
+	com.android.voicemail \
+	com.android.voicemail.impl \
+	com.android.voicemail.impl.configui \
+
 LOCAL_AAPT_FLAGS := $(filter-out $(EXCLUDE_EXTRA_PACKAGES),$(LOCAL_AAPT_FLAGS))
 LOCAL_AAPT_FLAGS := $(addprefix --extra-packages , $(LOCAL_AAPT_FLAGS))
 LOCAL_AAPT_FLAGS += \
@@ -174,13 +242,9 @@
 LOCAL_PROGUARD_ENABLED := disabled
 ifdef LOCAL_JACK_ENABLED
 
-define all-dialer-proguard-flags
-  $(shell cd $(LOCAL_PATH) ; \
-          find . -type f -name "proguard.*flags" | uniq | sort | cut -c 3-)
-endef
 
 # Proguard includes
-LOCAL_PROGUARD_FLAG_FILES := $(call all-dialer-proguard-flags)
+LOCAL_PROGUARD_FLAG_FILES := $(call all-named-files-under,proguard.*flags,$(SRC_DIRS))
 LOCAL_PROGUARD_ENABLED := custom
 
 LOCAL_PROGUARD_ENABLED += optimization
diff --git a/java/com/android/dialer/callcomposer/CallComposerActivity.java b/java/com/android/dialer/callcomposer/CallComposerActivity.java
index d785cf3..fa380cc 100644
--- a/java/com/android/dialer/callcomposer/CallComposerActivity.java
+++ b/java/com/android/dialer/callcomposer/CallComposerActivity.java
@@ -315,7 +315,11 @@
         break;
       case Session.STATE_START_FAILED:
       case Session.STATE_CLOSED:
-        setFailedResultAndFinish();
+        if (pendingCallStarted) {
+          placeTelecomCall();
+        } else {
+          setFailedResultAndFinish();
+        }
         break;
       case Session.STATE_MESSAGE_SENT:
         if (++messageSentCounter == 3) {
diff --git a/java/com/android/dialer/calldetails/CallDetailsActivity.java b/java/com/android/dialer/calldetails/CallDetailsActivity.java
index 385b4d1..569aaa5 100644
--- a/java/com/android/dialer/calldetails/CallDetailsActivity.java
+++ b/java/com/android/dialer/calldetails/CallDetailsActivity.java
@@ -182,11 +182,10 @@
     Map<CallDetailsEntry, List<HistoryResult>> mappedResults =
         getAllHistoricalData(contact.getNumber(), entries);
 
-    entries =
+    adapter.updateCallDetailsEntries(
         generateAndMapNewCallDetailsEntriesHistoryResults(
-            contact.getNumber(), entries, mappedResults);
-
-    adapter.updateCallDetailsEntries(entries.getEntriesList());
+                contact.getNumber(), entries, mappedResults)
+            .getEntriesList());
   }
 
   @NonNull
diff --git a/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java b/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java
index 78c329a..e1ec0f6 100644
--- a/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java
+++ b/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java
@@ -40,11 +40,12 @@
           .append(AnnotatedCallLog.TIMESTAMP + " integer, ")
           .append(AnnotatedCallLog.NAME + " string, ")
           .append(AnnotatedCallLog.FORMATTED_NUMBER + " string, ")
-          .append(AnnotatedCallLog.NEW + " integer, ")
-          .append(AnnotatedCallLog.TYPE + " integer, ")
-          .append(AnnotatedCallLog.CONTACT_PHOTO_URI + " string, ")
+          .append(AnnotatedCallLog.PHOTO_URI + " string, ")
+          .append(AnnotatedCallLog.PHOTO_ID + " integer, ")
+          .append(AnnotatedCallLog.LOOKUP_URI + " string, ")
           .append(AnnotatedCallLog.NUMBER_TYPE_LABEL + " string, ")
           .append(AnnotatedCallLog.IS_READ + " integer, ")
+          .append(AnnotatedCallLog.NEW + " integer, ")
           .append(AnnotatedCallLog.GEOCODED_LOCATION + " string, ")
           .append(AnnotatedCallLog.PHONE_ACCOUNT_LABEL + " string, ")
           .append(AnnotatedCallLog.PHONE_ACCOUNT_COLOR + " integer, ")
@@ -52,7 +53,8 @@
           .append(AnnotatedCallLog.IS_BUSINESS + " integer, ")
           .append(AnnotatedCallLog.IS_VOICEMAIL + " integer, ")
           // Columns only in AnnotatedCallLog
-          .append(AnnotatedCallLog.NUMBER + " blob")
+          .append(AnnotatedCallLog.NUMBER + " blob, ")
+          .append(AnnotatedCallLog.TYPE + " integer")
           .append(");")
           .toString();
 
diff --git a/java/com/android/dialer/calllog/database/Coalescer.java b/java/com/android/dialer/calllog/database/Coalescer.java
index 55bed3e..63fa9f8 100644
--- a/java/com/android/dialer/calllog/database/Coalescer.java
+++ b/java/com/android/dialer/calllog/database/Coalescer.java
@@ -32,6 +32,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import javax.inject.Inject;
 
 /**
@@ -129,7 +130,12 @@
    */
   private static boolean rowsShouldBeCombined(
       DialerPhoneNumberUtil dialerPhoneNumberUtil, ContentValues row1, ContentValues row2) {
-    // TODO(zachh): Real implementation.
+    // Don't combine rows which don't use the same phone account.
+    if (!Objects.equals(
+        row1.getAsString(AnnotatedCallLog.PHONE_ACCOUNT_LABEL),
+        row2.getAsString(AnnotatedCallLog.PHONE_ACCOUNT_LABEL))) {
+      return false;
+    }
     DialerPhoneNumber number1;
     DialerPhoneNumber number2;
     try {
diff --git a/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java b/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java
index be891c5..25950f6 100644
--- a/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java
+++ b/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java
@@ -42,8 +42,7 @@
     String TIMESTAMP = "timestamp";
 
     /**
-     * Name of the caller if available. This could be a name from a local contact or caller ID data
-     * source, for example.
+     * Copied from {@link android.provider.CallLog.Calls#CACHED_NAME}.
      *
      * <p>This is exactly how it should appear to the user. If the user's locale or name display
      * preferences change, this column should be rewritten.
@@ -60,14 +59,25 @@
     String FORMATTED_NUMBER = "formatted_number";
 
     /**
-     * Local photo URI for the contact associated with the phone number, if it exists.
-     *
-     * <p>Photos currently only come from local contacts database and not caller ID sources. If
-     * there is no photo for a contact then an appropriate letter tile should be drawn.
+     * Copied from {@link android.provider.CallLog.Calls#CACHED_PHOTO_URI}.
      *
      * <p>TYPE: TEXT
      */
-    String CONTACT_PHOTO_URI = "contact_photo_uri";
+    String PHOTO_URI = "photo_uri";
+
+    /**
+     * Copied from {@link android.provider.CallLog.Calls#CACHED_PHOTO_ID}.
+     *
+     * <p>Type: INTEGER (long)
+     */
+    String PHOTO_ID = "photo_id";
+
+    /**
+     * Copied from {@link android.provider.CallLog.Calls#CACHED_LOOKUP_URI}.
+     *
+     * <p>TYPE: TEXT
+     */
+    String LOOKUP_URI = "lookup_uri";
 
     // TODO(zachh): If we need to support photos other than local contacts', add a (blob?) column.
 
@@ -144,7 +154,9 @@
           TIMESTAMP,
           NAME,
           FORMATTED_NUMBER,
-          CONTACT_PHOTO_URI,
+          PHOTO_URI,
+          PHOTO_ID,
+          LOOKUP_URI,
           NUMBER_TYPE_LABEL,
           IS_READ,
           NEW,
diff --git a/java/com/android/dialer/calllog/datasources/contacts/ContactsDataSource.java b/java/com/android/dialer/calllog/datasources/contacts/ContactsDataSource.java
index 8090cbc..f0384b0 100644
--- a/java/com/android/dialer/calllog/datasources/contacts/ContactsDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/contacts/ContactsDataSource.java
@@ -20,10 +20,8 @@
 import android.content.Context;
 import android.support.annotation.MainThread;
 import android.support.annotation.WorkerThread;
-import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog;
 import com.android.dialer.calllog.datasources.CallLogDataSource;
 import com.android.dialer.calllog.datasources.CallLogMutations;
-import com.android.dialer.calllog.datasources.util.RowCombiner;
 import com.android.dialer.common.Assert;
 import java.util.List;
 import javax.inject.Inject;
@@ -50,9 +48,6 @@
       CallLogMutations mutations) {
     Assert.isWorkerThread();
     // TODO(zachh): Implementation.
-    for (ContentValues contentValues : mutations.getInserts().values()) {
-      contentValues.put(AnnotatedCallLog.NAME, "Placeholder name");
-    }
   }
 
   @Override
@@ -63,9 +58,7 @@
   @Override
   public ContentValues coalesce(List<ContentValues> individualRowsSortedByTimestampDesc) {
     // TODO(zachh): Implementation.
-    return new RowCombiner(individualRowsSortedByTimestampDesc)
-        .useSingleValueString(AnnotatedCallLog.NAME)
-        .combine();
+    return new ContentValues();
   }
 
   @MainThread
diff --git a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
index e4af417..1bdbb8a 100644
--- a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
@@ -29,6 +29,7 @@
 import android.provider.CallLog;
 import android.provider.CallLog.Calls;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.support.annotation.ColorInt;
 import android.support.annotation.MainThread;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
@@ -157,8 +158,14 @@
             .useMostRecentLong(AnnotatedCallLog.TIMESTAMP)
             .useMostRecentLong(AnnotatedCallLog.NEW)
             .useMostRecentString(AnnotatedCallLog.NUMBER_TYPE_LABEL)
-            .useMostRecentString(AnnotatedCallLog.GEOCODED_LOCATION)
+            .useMostRecentString(AnnotatedCallLog.NAME)
             .useMostRecentString(AnnotatedCallLog.FORMATTED_NUMBER)
+            .useMostRecentString(AnnotatedCallLog.PHOTO_URI)
+            .useMostRecentLong(AnnotatedCallLog.PHOTO_ID)
+            .useMostRecentString(AnnotatedCallLog.LOOKUP_URI)
+            .useMostRecentString(AnnotatedCallLog.GEOCODED_LOCATION)
+            .useSingleValueString(AnnotatedCallLog.PHONE_ACCOUNT_LABEL)
+            .useSingleValueLong(AnnotatedCallLog.PHONE_ACCOUNT_COLOR)
             .combine();
 
     CallTypes.Builder callTypes = CallTypes.newBuilder();
@@ -195,7 +202,11 @@
                   Calls.NUMBER,
                   Calls.TYPE,
                   Calls.COUNTRY_ISO,
+                  Calls.CACHED_NAME,
                   Calls.CACHED_FORMATTED_NUMBER,
+                  Calls.CACHED_PHOTO_URI,
+                  Calls.CACHED_PHOTO_ID,
+                  Calls.CACHED_LOOKUP_URI,
                   Calls.CACHED_NUMBER_TYPE,
                   Calls.CACHED_NUMBER_LABEL,
                   Calls.IS_READ,
@@ -226,8 +237,12 @@
         int numberColumn = cursor.getColumnIndexOrThrow(Calls.NUMBER);
         int typeColumn = cursor.getColumnIndexOrThrow(Calls.TYPE);
         int countryIsoColumn = cursor.getColumnIndexOrThrow(Calls.COUNTRY_ISO);
+        int cachedNameColumn = cursor.getColumnIndexOrThrow(Calls.CACHED_NAME);
         int cachedFormattedNumberColumn =
             cursor.getColumnIndexOrThrow(Calls.CACHED_FORMATTED_NUMBER);
+        int cachedPhotoUriColumn = cursor.getColumnIndexOrThrow(Calls.CACHED_PHOTO_URI);
+        int cachedPhotoIdColumn = cursor.getColumnIndexOrThrow(Calls.CACHED_PHOTO_ID);
+        int cachedLookupUriColumn = cursor.getColumnIndexOrThrow(Calls.CACHED_LOOKUP_URI);
         int cachedNumberTypeColumn = cursor.getColumnIndexOrThrow(Calls.CACHED_NUMBER_TYPE);
         int cachedNumberLabelColumn = cursor.getColumnIndexOrThrow(Calls.CACHED_NUMBER_LABEL);
         int isReadColumn = cursor.getColumnIndexOrThrow(Calls.IS_READ);
@@ -245,9 +260,13 @@
           long id = cursor.getLong(idColumn);
           long date = cursor.getLong(dateColumn);
           String numberAsStr = cursor.getString(numberColumn);
-          long type = cursor.getType(typeColumn);
+          long type = cursor.getInt(typeColumn);
           String countryIso = cursor.getString(countryIsoColumn);
+          String cachedName = cursor.getString(cachedNameColumn);
           String formattedNumber = cursor.getString(cachedFormattedNumberColumn);
+          String cachedPhotoUri = cursor.getString(cachedPhotoUriColumn);
+          long cachedPhotoId = cursor.getLong(cachedPhotoIdColumn);
+          String cachedLookupUri = cursor.getString(cachedLookupUriColumn);
           int cachedNumberType = cursor.getInt(cachedNumberTypeColumn);
           String cachedNumberLabel = cursor.getString(cachedNumberLabelColumn);
           int isRead = cursor.getInt(isReadColumn);
@@ -268,7 +287,11 @@
           }
 
           contentValues.put(AnnotatedCallLog.TYPE, type);
+          contentValues.put(AnnotatedCallLog.NAME, cachedName);
           contentValues.put(AnnotatedCallLog.FORMATTED_NUMBER, formattedNumber);
+          contentValues.put(AnnotatedCallLog.PHOTO_URI, cachedPhotoUri);
+          contentValues.put(AnnotatedCallLog.PHOTO_ID, cachedPhotoId);
+          contentValues.put(AnnotatedCallLog.LOOKUP_URI, cachedLookupUri);
 
           // Phone.getTypeLabel returns "Custom" if given (0, null) which is not of any use. Just
           // omit setting the label if there's no information for it.
@@ -311,9 +334,12 @@
     }
     contentValues.put(AnnotatedCallLog.PHONE_ACCOUNT_LABEL, label);
 
-    int color = PhoneAccountUtils.getAccountColor(appContext, phoneAccountHandle);
+    @ColorInt int color = PhoneAccountUtils.getAccountColor(appContext, phoneAccountHandle);
     if (color == PhoneAccount.NO_HIGHLIGHT_COLOR) {
-      color = R.color.dialer_secondary_text_color;
+      color =
+          appContext
+              .getResources()
+              .getColor(R.color.dialer_secondary_text_color, appContext.getTheme());
     }
     contentValues.put(AnnotatedCallLog.PHONE_ACCOUNT_COLOR, color);
   }
diff --git a/java/com/android/dialer/calllog/datasources/util/RowCombiner.java b/java/com/android/dialer/calllog/datasources/util/RowCombiner.java
index 3595a05..adb7a07 100644
--- a/java/com/android/dialer/calllog/datasources/util/RowCombiner.java
+++ b/java/com/android/dialer/calllog/datasources/util/RowCombiner.java
@@ -19,6 +19,7 @@
 import com.android.dialer.common.Assert;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 
 /** Convenience class for aggregating row values. */
 public class RowCombiner {
@@ -48,11 +49,19 @@
     String singleValue = iterator.next().getAsString(columnName);
     while (iterator.hasNext()) {
       String current = iterator.next().getAsString(columnName);
-      if (current == null) {
-        Assert.checkState(singleValue == null);
-      } else {
-        Assert.checkState(current.equals(singleValue));
-      }
+      Assert.checkState(Objects.equals(singleValue, current), "Values different for " + columnName);
+    }
+    combinedRow.put(columnName, singleValue);
+    return this;
+  }
+
+  /** Asserts that all column values for the given column name are the same, and uses it. */
+  public RowCombiner useSingleValueLong(String columnName) {
+    Iterator<ContentValues> iterator = individualRowsSortedByTimestampDesc.iterator();
+    Long singleValue = iterator.next().getAsLong(columnName);
+    while (iterator.hasNext()) {
+      Long current = iterator.next().getAsLong(columnName);
+      Assert.checkState(Objects.equals(singleValue, current), "Values different for " + columnName);
     }
     combinedRow.put(columnName, singleValue);
     return this;
diff --git a/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java b/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java
index 51a5532..d893383 100644
--- a/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java
+++ b/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java
@@ -18,9 +18,12 @@
 
 import android.content.Context;
 import android.database.Cursor;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
 import android.support.v4.content.CursorLoader;
 import com.android.dialer.CallTypes;
 import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.CoalescedAnnotatedCallLog;
+import com.android.dialer.common.Assert;
 import com.google.protobuf.InvalidProtocolBufferException;
 
 /** CursorLoader for the coalesced annotated call log. */
@@ -32,18 +35,20 @@
   private static final int TIMESTAMP = 1;
   private static final int NAME = 2;
   private static final int FORMATTED_NUMBER = 3;
-  private static final int CONTACT_PHOTO_URI = 4;
-  private static final int NUMBER_TYPE_LABEL = 5;
-  private static final int IS_READ = 6;
-  private static final int NEW = 7;
-  private static final int GEOCODED_LOCATION = 8;
-  private static final int PHONE_ACCOUNT_LABEL = 9;
-  private static final int PHONE_ACCOUNT_COLOR = 10;
-  private static final int FEATURES = 11;
-  private static final int IS_BUSINESS = 12;
-  private static final int IS_VOICEMAIL = 13;
-  private static final int NUMBER_CALLS = 14;
-  private static final int CALL_TYPES = 15;
+  private static final int PHOTO_URI = 4;
+  private static final int PHOTO_ID = 5;
+  private static final int LOOKUP_URI = 6;
+  private static final int NUMBER_TYPE_LABEL = 7;
+  private static final int IS_READ = 8;
+  private static final int NEW = 9;
+  private static final int GEOCODED_LOCATION = 10;
+  private static final int PHONE_ACCOUNT_LABEL = 11;
+  private static final int PHONE_ACCOUNT_COLOR = 12;
+  private static final int FEATURES = 13;
+  private static final int IS_BUSINESS = 14;
+  private static final int IS_VOICEMAIL = 15;
+  private static final int NUMBER_CALLS = 16;
+  private static final int CALL_TYPES = 17;
 
   /** Convenience class for accessing values using an abbreviated syntax. */
   static final class Row {
@@ -65,8 +70,20 @@
       return cursor.getString(NAME);
     }
 
-    String contactPhotoUri() {
-      return cursor.getString(CONTACT_PHOTO_URI);
+    String formattedNumber() {
+      return cursor.getString(FORMATTED_NUMBER);
+    }
+
+    String photoUri() {
+      return cursor.getString(PHOTO_URI);
+    }
+
+    long photoId() {
+      return cursor.getLong(PHOTO_ID);
+    }
+
+    String lookupUri() {
+      return cursor.getString(LOOKUP_URI);
     }
 
     String numberTypeLabel() {
@@ -89,6 +106,7 @@
       return cursor.getString(PHONE_ACCOUNT_LABEL);
     }
 
+    @ColorInt
     int phoneAccountColor() {
       return cursor.getInt(PHONE_ACCOUNT_COLOR);
     }
@@ -109,12 +127,13 @@
       return cursor.getInt(NUMBER_CALLS);
     }
 
-    String formattedNumber() {
-      return cursor.getString(FORMATTED_NUMBER);
-    }
-
-    CallTypes callTypes() throws InvalidProtocolBufferException {
-      return CallTypes.parseFrom(cursor.getBlob(CALL_TYPES));
+    @NonNull
+    CallTypes callTypes() {
+      try {
+        return CallTypes.parseFrom(cursor.getBlob(CALL_TYPES));
+      } catch (InvalidProtocolBufferException e) {
+        throw Assert.createAssertionFailException("Couldn't parse call types", e);
+      }
     }
   }
 
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java b/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
index b6b658f..e198a38 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
@@ -17,15 +17,19 @@
 
 import android.content.Context;
 import android.database.Cursor;
+import android.net.Uri;
 import android.provider.CallLog.Calls;
 import android.support.v7.widget.RecyclerView;
 import android.text.TextUtils;
 import android.view.View;
 import android.widget.QuickContactBadge;
 import android.widget.TextView;
+import com.android.dialer.calllog.ui.CoalescedAnnotatedCallLogCursorLoader.Row;
 import com.android.dialer.calllogutils.CallLogDates;
+import com.android.dialer.calllogutils.CallTypeIconsView;
 import com.android.dialer.contactphoto.ContactPhotoManager;
 import com.android.dialer.lettertile.LetterTileDrawable;
+import com.android.dialer.oem.MotorolaUtils;
 import com.android.dialer.time.Clock;
 import java.util.Locale;
 
@@ -36,6 +40,9 @@
   private final TextView primaryTextView;
   private final TextView secondaryTextView;
   private final QuickContactBadge quickContactBadge;
+  private final CallTypeIconsView primaryCallTypeIconsView; // Used for Wifi, HD icons
+  private final CallTypeIconsView secondaryCallTypeIconsView; // Used for call types
+  private final TextView phoneAccountView;
   private final Clock clock;
 
   NewCallLogViewHolder(View view, Clock clock) {
@@ -44,6 +51,9 @@
     primaryTextView = view.findViewById(R.id.primary_text);
     secondaryTextView = view.findViewById(R.id.secondary_text);
     quickContactBadge = view.findViewById(R.id.quick_contact_photo);
+    primaryCallTypeIconsView = view.findViewById(R.id.primary_call_type_icons);
+    secondaryCallTypeIconsView = view.findViewById(R.id.secondary_call_type_icons);
+    phoneAccountView = view.findViewById(R.id.phone_account);
     this.clock = clock;
   }
 
@@ -52,10 +62,6 @@
     CoalescedAnnotatedCallLogCursorLoader.Row row =
         new CoalescedAnnotatedCallLogCursorLoader.Row(cursor);
 
-    // TODO(zachh): Add HD icon and Wifi icon after primary text.
-    // TODO(zachh): Call type icons for last 3 calls.
-    // TODO(zachh): Use name for primary text if available.
-    // TODO(zachh): Handle CallLog.Calls.PRESENTATION_*, including Verizon restricted numbers.
     // TODO(zachh): Handle RTL properly.
     primaryTextView.setText(buildPrimaryText(row));
     secondaryTextView.setText(buildSecondaryText(row));
@@ -67,15 +73,22 @@
       secondaryTextView.setTextAppearance(R.style.secondary_textview_new_call);
     }
 
-    setPhoto();
+    setPhoto(row);
+    setPrimaryCallTypes(row);
+    setSecondaryCallTypes(row);
+    setPhoneAccounts(row);
   }
 
   private String buildPrimaryText(CoalescedAnnotatedCallLogCursorLoader.Row row) {
-    StringBuilder primaryText =
-        new StringBuilder(
-            TextUtils.isEmpty(row.formattedNumber())
-                ? context.getText(R.string.new_call_log_unknown)
-                : row.formattedNumber());
+    StringBuilder primaryText = new StringBuilder();
+    if (!TextUtils.isEmpty(row.name())) {
+      primaryText.append(row.name());
+    } else if (!TextUtils.isEmpty(row.formattedNumber())) {
+      primaryText.append(row.formattedNumber());
+    } else {
+      // TODO(zachh): Handle CallLog.Calls.PRESENTATION_*, including Verizon restricted numbers.
+      primaryText.append(context.getText(R.string.new_call_log_unknown));
+    }
     if (row.numberCalls() > 1) {
       primaryText.append(String.format(Locale.getDefault(), " (%d)", row.numberCalls()));
     }
@@ -124,10 +137,39 @@
     return secondaryText.toString();
   }
 
-  private void setPhoto() {
-    // TODO(zachh): Set photo/icon appropriately. (This just uses the anonymous avatar.)
+  private void setPhoto(Row row) {
+    // TODO(zachh): Set the contact type.
     ContactPhotoManager.getInstance(context)
         .loadDialerThumbnailOrPhoto(
-            quickContactBadge, null, 0, null, null, LetterTileDrawable.TYPE_DEFAULT);
+            quickContactBadge,
+            row.lookupUri() == null ? null : Uri.parse(row.lookupUri()),
+            row.photoId(),
+            row.photoUri() == null ? null : Uri.parse(row.photoUri()),
+            row.name(),
+            LetterTileDrawable.TYPE_DEFAULT);
+  }
+
+  private void setPrimaryCallTypes(Row row) {
+    // Only HD and Wifi icons are shown following the primary text.
+    primaryCallTypeIconsView.setShowHd(
+        MotorolaUtils.shouldShowHdIconInCallLog(context, row.features()));
+    primaryCallTypeIconsView.setShowWifi(
+        MotorolaUtils.shouldShowWifiIconInCallLog(context, row.features()));
+  }
+
+  private void setSecondaryCallTypes(Row row) {
+    // Only call type icons are shown before the secondary text.
+    for (int callType : row.callTypes().getTypeList()) {
+      secondaryCallTypeIconsView.add(callType);
+    }
+    // TODO(zachh): Per new mocks, may need to add method to CallTypeIconsView to disable coloring.
+  }
+
+  private void setPhoneAccounts(Row row) {
+    if (row.phoneAccountLabel() != null) {
+      phoneAccountView.setText(row.phoneAccountLabel());
+      phoneAccountView.setTextColor(row.phoneAccountColor());
+      phoneAccountView.setVisibility(View.VISIBLE);
+    }
   }
 }
diff --git a/java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml b/java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml
index 568b351..77ba681 100644
--- a/java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml
+++ b/java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml
@@ -42,19 +42,52 @@
       android:layout_toStartOf="@+id/menu_button"
       android:orientation="vertical">
 
-    <TextView
-        android:id="@+id/primary_text"
-        style="@style/PrimaryText"
+    <!-- TODO(zachh): Optimize this layout -->
+    <LinearLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginStart="@dimen/call_log_entry_photo_text_margin"/>
+        android:orientation="horizontal">
 
-    <TextView
-        android:id="@+id/secondary_text"
-        style="@style/SecondaryText"
+      <TextView
+          android:id="@+id/primary_text"
+          style="@style/PrimaryText"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_marginStart="@dimen/call_log_entry_photo_text_margin"/>
+
+      <!-- HD and Wifi icons are shown adjacent to primary text. Call types are shown adjacent to
+           secondary text (below). -->
+      <com.android.dialer.calllogutils.CallTypeIconsView
+          android:id="@+id/primary_call_type_icons"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_marginStart="12dp"
+          android:layout_gravity="center_vertical"/>
+
+    </LinearLayout>
+
+    <!-- TODO(zachh): Optimize this layout -->
+    <LinearLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginStart="@dimen/call_log_entry_photo_text_margin"/>
+        android:orientation="horizontal">
+
+      <!-- Only call types are shown adjacent to secondary text. HD and Wifi icons are shown
+           adjacent to primary text (above). -->
+      <com.android.dialer.calllogutils.CallTypeIconsView
+          android:id="@+id/secondary_call_type_icons"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_marginStart="12dp"
+          android:layout_gravity="center_vertical"/>
+
+      <TextView
+          android:id="@+id/secondary_text"
+          style="@style/SecondaryText"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_marginStart="@dimen/call_log_entry_photo_text_margin"/>
+    </LinearLayout>
 
     <TextView
         android:id="@+id/phone_account"
diff --git a/java/com/android/dialer/common/concurrent/DialerUiTaskFragment.java b/java/com/android/dialer/common/concurrent/DialerUiTaskFragment.java
index b6068b2..f0b7537 100644
--- a/java/com/android/dialer/common/concurrent/DialerUiTaskFragment.java
+++ b/java/com/android/dialer/common/concurrent/DialerUiTaskFragment.java
@@ -146,14 +146,38 @@
       if (successListener == null) {
         LogUtil.i("DialerUiTaskFragment.runTask", "task succeeded but UI is dead");
       } else {
-        ThreadUtil.postOnUiThread(() -> successListener.onSuccess(output));
+        ThreadUtil.postOnUiThread(
+            () -> {
+              // Even though there is a null check above, it is possible for the activity/fragment
+              // to be finished between the time the runnable is posted and the time it executes. Do
+              // an additional check here.
+              if (successListener == null) {
+                LogUtil.i(
+                    "DialerUiTaskFragment.runTask",
+                    "task succeeded but UI died after success runnable posted");
+              } else {
+                successListener.onSuccess(output);
+              }
+            });
       }
     } catch (Throwable throwable) {
       LogUtil.e("DialerUiTaskFragment.runTask", "task failed", throwable);
       if (failureListener == null) {
         LogUtil.i("DialerUiTaskFragment.runTask", "task failed but UI is dead");
       } else {
-        ThreadUtil.postOnUiThread(() -> failureListener.onFailure(throwable));
+        ThreadUtil.postOnUiThread(
+            () -> {
+              // Even though there is a null check above, it is possible for the activity/fragment
+              // to be finished between the time the runnable is posted and the time it executes. Do
+              // an additional check here.
+              if (failureListener == null) {
+                LogUtil.i(
+                    "DialerUiTaskFragment.runTask",
+                    "task failed but UI died after failure runnable posted");
+              } else {
+                failureListener.onFailure(throwable);
+              }
+            });
       }
     }
   }
diff --git a/java/com/android/dialer/contactsfragment/ContactsFragment.java b/java/com/android/dialer/contactsfragment/ContactsFragment.java
index 86ac834..ddf00b3 100644
--- a/java/com/android/dialer/contactsfragment/ContactsFragment.java
+++ b/java/com/android/dialer/contactsfragment/ContactsFragment.java
@@ -67,9 +67,7 @@
 
   /** An enum for the different types of headers that be inserted at position 0 in the list. */
   @Retention(RetentionPolicy.SOURCE)
-  @IntDef({
-  ContactsFragment.Header.NONE,
-  ContactsFragment.Header.ADD_CONTACT})
+  @IntDef({Header.NONE, Header.ADD_CONTACT})
   public @interface Header {
     int NONE = 0;
     /** Header that allows the user to add a new contact. */
diff --git a/java/com/android/dialer/location/CountryDetector.java b/java/com/android/dialer/location/CountryDetector.java
index c4bed14..110cf45 100644
--- a/java/com/android/dialer/location/CountryDetector.java
+++ b/java/com/android/dialer/location/CountryDetector.java
@@ -72,7 +72,7 @@
   // exceedingly rare event that the device does not have a default locale set for some reason.
   private static final String DEFAULT_COUNTRY_ISO = "US";
 
-  @VisibleForTesting static CountryDetector sInstance;
+  @VisibleForTesting public static CountryDetector sInstance;
 
   private final TelephonyManager telephonyManager;
   private final LocaleProvider localeProvider;
@@ -80,7 +80,7 @@
   private final Context appContext;
 
   @VisibleForTesting
-  CountryDetector(
+  public CountryDetector(
       Context appContext,
       TelephonyManager telephonyManager,
       LocationManager locationManager,
@@ -192,7 +192,7 @@
   }
 
   /** Interface for accessing the current locale. */
-  interface LocaleProvider {
+  public interface LocaleProvider {
     Locale getLocale();
   }
 
diff --git a/java/com/android/dialer/strictmode/DialerStrictMode.java b/java/com/android/dialer/strictmode/DialerStrictMode.java
index c7d0b3f..f895f7c 100644
--- a/java/com/android/dialer/strictmode/DialerStrictMode.java
+++ b/java/com/android/dialer/strictmode/DialerStrictMode.java
@@ -145,5 +145,3 @@
     runnable.run();
   }
 }
-
-
diff --git a/java/com/android/incallui/callpending/CallPendingActivity.java b/java/com/android/incallui/callpending/CallPendingActivity.java
index d08998b..26490d9 100644
--- a/java/com/android/incallui/callpending/CallPendingActivity.java
+++ b/java/com/android/incallui/callpending/CallPendingActivity.java
@@ -28,6 +28,7 @@
 import android.telecom.TelecomManager;
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.enrichedcall.EnrichedCallComponent;
+import com.android.dialer.enrichedcall.Session;
 import com.android.dialer.multimedia.MultimediaData;
 import com.android.incallui.audiomode.AudioModeProvider;
 import com.android.incallui.call.DialerCall.State;
@@ -152,11 +153,15 @@
   }
 
   private PrimaryInfo createPrimaryInfo() {
-    MultimediaData multimediaData =
-        EnrichedCallComponent.get(this)
-            .getEnrichedCallManager()
-            .getSession(getSessionId())
-            .getMultimediaData();
+    Session session =
+        EnrichedCallComponent.get(this).getEnrichedCallManager().getSession(getSessionId());
+    MultimediaData multimediaData;
+    if (session == null) {
+      LogUtil.i("CallPendingActivity.createPrimaryInfo", "Null session.");
+      multimediaData = null;
+    } else {
+      multimediaData = session.getMultimediaData();
+    }
 
     Drawable photo = null;
     try {
diff --git a/java/com/android/incallui/videotech/lightbringer/LightbringerTech.java b/java/com/android/incallui/videotech/lightbringer/LightbringerTech.java
index 26d6347..4882ba8 100644
--- a/java/com/android/incallui/videotech/lightbringer/LightbringerTech.java
+++ b/java/com/android/incallui/videotech/lightbringer/LightbringerTech.java
@@ -23,6 +23,7 @@
 import android.telecom.Call;
 import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
 import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
 import com.android.dialer.configprovider.ConfigProviderBindings;
 import com.android.dialer.lightbringer.Lightbringer;
 import com.android.dialer.lightbringer.LightbringerListener;
@@ -54,11 +55,33 @@
 
   @Override
   public boolean isAvailable(Context context) {
-    return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
-        && ConfigProviderBindings.get(context).getBoolean("enable_lightbringer_video_upgrade", true)
-        && callState == Call.STATE_ACTIVE
-        && lightbringer.supportsUpgrade(context, callingNumber)
-        && TelecomManagerCompat.supportsHandover();
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+      LogUtil.v("LightbringerTech.isAvailable", "upgrade unavailable, only supported on O+");
+      return false;
+    }
+
+    if (!ConfigProviderBindings.get(context)
+        .getBoolean("enable_lightbringer_video_upgrade", true)) {
+      LogUtil.v("LightbringerTech.isAvailable", "upgrade disabled by flag");
+      return false;
+    }
+
+    if (callState != Call.STATE_ACTIVE) {
+      LogUtil.v("LightbringerTech.isAvailable", "upgrade unavailable, call must be active");
+      return false;
+    }
+
+    if (!TelecomManagerCompat.supportsHandover()) {
+      LogUtil.v("LightbringerTech.isAvailable", "upgrade unavailable, telephony support missing");
+      return false;
+    }
+
+    if (!lightbringer.supportsUpgrade(context, callingNumber)) {
+      LogUtil.v("LightbringerTech.isAvailable", "upgrade unavailable, number does not support it");
+      return false;
+    }
+
+    return true;
   }
 
   @Override