Change PhoneCallDetail fields to be non-final.

It's been a pain to add/change fields on PhoneCallDetails because
a multitude of parameters required for the constructors to create
an instance. I ran into this while considering how to add an
objectId to its parameters, and have previously too...

Make fields non-final so that they are more easily set. This has
the side-effect of making the casing of some initialization code
more straightforward.

+ Change it's constructor to a subset of required fields.
+ Simplify/reorganize CallLogAdapter and CallLogAsyncTaskUtil code.
+ Simplify tests.

Bug: 21733599
Change-Id: I236dfb0b8e6513f4b44dbdae17ce2eb9c9ae4778
diff --git a/src/com/android/dialer/PhoneCallDetails.java b/src/com/android/dialer/PhoneCallDetails.java
index 843e193..68fdadc 100644
--- a/src/com/android/dialer/PhoneCallDetails.java
+++ b/src/com/android/dialer/PhoneCallDetails.java
@@ -16,7 +16,6 @@
 
 package com.android.dialer;
 
-import com.google.common.annotations.VisibleForTesting;
 import com.android.dialer.calllog.PhoneNumberDisplayUtil;
 
 import android.content.Context;
@@ -29,124 +28,81 @@
 
 /**
  * The details of a phone call to be shown in the UI.
- *
- * TODO: Create a builder, to make it easier to construct an instance.
  */
 public class PhoneCallDetails {
-    /** The number of the other party involved in the call. */
-    public final CharSequence number;
-    /** The number presenting rules set by the network, e.g., {@link Calls#PRESENTATION_ALLOWED} */
-    public final int numberPresentation;
-    /** The formatted version of {@link #number}. */
-    public final CharSequence formattedNumber;
-    /** The country corresponding with the phone number. */
-    public final String countryIso;
-    /** The geocoded location for the phone number. */
-    public final String geocode;
+    // The number of the other party involved in the call.
+    public CharSequence number;
+    // The number presenting rules set by the network, e.g., {@link Calls#PRESENTATION_ALLOWED}
+    public int numberPresentation;
+    // The formatted version of {@link #number}.
+    public CharSequence formattedNumber;
+    // The country corresponding with the phone number.
+    public String countryIso;
+    // The geocoded location for the phone number.
+    public String geocode;
+
     /**
      * The type of calls, as defined in the call log table, e.g., {@link Calls#INCOMING_TYPE}.
      * <p>
      * There might be multiple types if this represents a set of entries grouped together.
      */
-    public final int[] callTypes;
-    /** The date of the call, in milliseconds since the epoch. */
-    public final long date;
-    /** The duration of the call in milliseconds, or 0 for missed calls. */
-    public final long duration;
-    /** The name of the contact, or the empty string. */
-    public final CharSequence name;
-    /** The type of phone, e.g., {@link Phone#TYPE_HOME}, 0 if not available. */
-    public final int numberType;
-    /** The custom label associated with the phone number in the contact, or the empty string. */
-    public final CharSequence numberLabel;
-    /** The URI of the contact associated with this phone call. */
-    public final Uri contactUri;
+    public int[] callTypes;
+
+    // The date of the call, in milliseconds since the epoch.
+    public long date;
+    // The duration of the call in milliseconds, or 0 for missed calls.
+    public long duration;
+    // The name of the contact, or the empty string.
+    public CharSequence name;
+    // The type of phone, e.g., {@link Phone#TYPE_HOME}, 0 if not available.
+    public int numberType;
+    // The custom label associated with the phone number in the contact, or the empty string.
+    public CharSequence numberLabel;
+    // The URI of the contact associated with this phone call.
+    public Uri contactUri;
     /**
      * The photo URI of the picture of the contact that is associated with this phone call or
      * null if there is none.
      * <p>
      * This is meant to store the high-res photo only.
      */
-    public final Uri photoUri;
-    /**
-     * The source type of the contact associated with this call.
-     */
-    public final int sourceType;
+    public Uri photoUri;
+
+    // The source type of the contact associated with this call.
+    public int sourceType;
+
+    // The unique identifier for the account associated with the call.
+    public PhoneAccountHandle accountHandle;
+
+    // Features applicable to this call.
+    public int features;
+
+    // Total data usage for this call.
+    public Long dataUsage;
+
+    // Voicemail transcription
+    public String transcription;
+
+    public String displayNumber;
+    public boolean isVoicemail;
 
     /**
-     * The unique identifier for the account associated with the call.
+     * Constructor with required fields for the details of a call with a number associated with a
+     * contact.
      */
-    public final PhoneAccountHandle accountHandle;
-    /**
-     * Features applicable to this call.
-     */
-    public final int features;
-    /**
-     * Total data usage for this call.
-     */
-    public final Long dataUsage;
-    /**
-     * Voicemail transcription
-     */
-    public final String transcription;
-
-    public final String displayNumber;
-    public final boolean isVoicemail;
-
-    /**
-     * Create the details for a call, with empty defaults specified for extra fields that are
-     * not necessary for testing.
-     */
-    @VisibleForTesting
-    public PhoneCallDetails(Context context, CharSequence number, int numberPresentation,
-            CharSequence formattedNumber, String countryIso, String geocode,
-            int[] callTypes, long date, long duration, boolean isVoicemail) {
-        this(context, number, numberPresentation, formattedNumber, countryIso, geocode,
-                callTypes, date, duration, "", 0, "", null, null, 0, null, 0, null, null,
-                isVoicemail);
-    }
-
-    /** Create the details for a call with a number not associated with a contact. */
-    public PhoneCallDetails(Context context, CharSequence number, int numberPresentation,
-            CharSequence formattedNumber, String countryIso, String geocode,
-            int[] callTypes, long date, long duration,
-            PhoneAccountHandle accountHandle, int features, Long dataUsage, String transcription,
+    public PhoneCallDetails(
+            Context context,
+            CharSequence number,
+            int numberPresentation,
+            CharSequence formattedNumber,
             boolean isVoicemail) {
-        this(context, number, numberPresentation, formattedNumber, countryIso, geocode,
-                callTypes, date, duration, "", 0, "", null, null, 0, accountHandle, features,
-                dataUsage, transcription, isVoicemail);
-    }
-
-    /** Create the details for a call with a number associated with a contact. */
-    public PhoneCallDetails(Context context, CharSequence number, int numberPresentation,
-            CharSequence formattedNumber, String countryIso, String geocode,
-            int[] callTypes, long date, long duration, CharSequence name,
-            int numberType, CharSequence numberLabel, Uri contactUri, Uri photoUri,
-            int sourceType, PhoneAccountHandle accountHandle, int features, Long dataUsage,
-            String transcription, boolean isVoicemail) {
         this.number = number;
         this.numberPresentation = numberPresentation;
         this.formattedNumber = formattedNumber;
-        this.countryIso = countryIso;
-        this.geocode = geocode;
-        this.callTypes = callTypes;
-        this.date = date;
-        this.duration = duration;
-        this.name = name;
-        this.numberType = numberType;
-        this.numberLabel = numberLabel;
-        this.contactUri = contactUri;
-        this.photoUri = photoUri;
-        this.sourceType = sourceType;
-        this.accountHandle = accountHandle;
-        this.features = features;
-        this.dataUsage = dataUsage;
-        this.transcription = transcription;
         this.isVoicemail = isVoicemail;
 
         this.displayNumber = PhoneNumberDisplayUtil.getDisplayNumber(
                 context,
-                this.accountHandle,
                 this.number,
                 this.numberPresentation,
                 this.formattedNumber,
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index 6862f68..daa7350 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -350,46 +350,13 @@
         }
         int count = getGroupSize(position);
 
-        CallLogListItemViewHolder views = (CallLogListItemViewHolder) viewHolder;
-
-        // Default case: an item in the call log.
-        views.primaryActionView.setVisibility(View.VISIBLE);
-
         final String number = c.getString(CallLogQuery.NUMBER);
         final int numberPresentation = c.getInt(CallLogQuery.NUMBER_PRESENTATION);
-        final long date = c.getLong(CallLogQuery.DATE);
-        final long duration = c.getLong(CallLogQuery.DURATION);
-        final int callType = c.getInt(CallLogQuery.CALL_TYPE);
         final PhoneAccountHandle accountHandle = PhoneAccountUtils.getAccount(
                 c.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME),
                 c.getString(CallLogQuery.ACCOUNT_ID));
         final String countryIso = c.getString(CallLogQuery.COUNTRY_ISO);
-
-        final long rowId = c.getLong(CallLogQuery.ID);
-        views.rowId = rowId;
-
-        // Check if the day group has changed and display a header if necessary.
-        int currentGroup = getDayGroupForCall(rowId);
-        int previousGroup = getPreviousDayGroup(c);
-        if (currentGroup != previousGroup) {
-            views.dayGroupHeader.setVisibility(View.VISIBLE);
-            views.dayGroupHeader.setText(getGroupDescription(currentGroup));
-        } else {
-            views.dayGroupHeader.setVisibility(View.GONE);
-        }
-
-        // Store some values used when the actions ViewStub is inflated on expansion of the actions
-        // section.
-        views.number = number;
-        views.numberPresentation = numberPresentation;
-        views.callType = callType;
-        views.accountHandle = accountHandle;
-        views.voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
-        // Stash away the Ids of the calls so that we can support deleting a row in the call log.
-        views.callIds = getCallIds(c, count);
-
         final ContactInfo cachedContactInfo = mContactInfoHelper.getContactInfo(c);
-
         final boolean isVoicemailNumber =
                 mPhoneNumberUtilsWrapper.isVoicemailNumber(accountHandle, number);
 
@@ -402,66 +369,81 @@
             // Lookup contacts with this number
             info = mContactInfoCache.getValue(number, countryIso, cachedContactInfo);
         }
-
-        final Uri lookupUri = info.lookupUri;
-        final String name = info.name;
-        final int ntype = info.type;
-        final String label = info.label;
-        final long photoId = info.photoId;
-        final Uri photoUri = info.photoUri;
         CharSequence formattedNumber = info.formattedNumber == null
                 ? null : PhoneNumberUtils.createTtsSpannable(info.formattedNumber);
-        final int[] callTypes = getCallTypes(c, count);
-        final String geocode = c.getString(CallLogQuery.GEOCODED_LOCATION);
-        final int sourceType = info.sourceType;
-        final int features = getCallFeatures(c, count);
-        final String transcription = c.getString(CallLogQuery.TRANSCRIPTION);
-        Long dataUsage = null;
+
+        final PhoneCallDetails details = new PhoneCallDetails(
+                mContext, number, numberPresentation, formattedNumber, isVoicemailNumber);
+        details.accountHandle = accountHandle;
+        details.callTypes = getCallTypes(c, count);
+        details.countryIso = countryIso;
+        details.date = c.getLong(CallLogQuery.DATE);
+        details.duration = c.getLong(CallLogQuery.DURATION);
+        details.features = getCallFeatures(c, count);
+        details.geocode = c.getString(CallLogQuery.GEOCODED_LOCATION);
+        details.transcription = c.getString(CallLogQuery.TRANSCRIPTION);
+
         if (!c.isNull(CallLogQuery.DATA_USAGE)) {
-            dataUsage = c.getLong(CallLogQuery.DATA_USAGE);
+            details.dataUsage = c.getLong(CallLogQuery.DATA_USAGE);
         }
 
-        final PhoneCallDetails details;
+        if (!TextUtils.isEmpty(info.name)) {
+            details.contactUri = info.lookupUri;
+            details.name = info.name;
+            details.numberType = info.type;
+            details.numberLabel = info.label;
+            details.photoUri = info.photoUri;
+            details.sourceType = info.sourceType;
+        }
 
+        CallLogListItemViewHolder views = (CallLogListItemViewHolder) viewHolder;
         views.info = info;
+        views.rowId = c.getLong(CallLogQuery.ID);
+        // Store values used when the actions ViewStub is inflated on expansion.
+        views.number = number;
+        views.numberPresentation = numberPresentation;
+        views.callType = c.getInt(CallLogQuery.CALL_TYPE);
+        views.accountHandle = accountHandle;
+        views.voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
+        // Stash away the Ids of the calls so that we can support deleting a row in the call log.
+        views.callIds = getCallIds(c, count);
 
         // The entry can only be reported as invalid if it has a valid ID and the source of the
         // entry supports marking entries as invalid.
-        views.canBeReportedAsInvalid = mContactInfoHelper.canReportAsInvalid(info.sourceType,
-                info.objectId);
+        views.canBeReportedAsInvalid = mContactInfoHelper.canReportAsInvalid(
+                info.sourceType, info.objectId);
+
+        // Default case: an item in the call log.
+        views.primaryActionView.setVisibility(View.VISIBLE);
+
+        // Check if the day group has changed and display a header if necessary.
+        int currentGroup = getDayGroupForCall(views.rowId);
+        int previousGroup = getPreviousDayGroup(c);
+        if (currentGroup != previousGroup) {
+            views.dayGroupHeader.setVisibility(View.VISIBLE);
+            views.dayGroupHeader.setText(getGroupDescription(currentGroup));
+        } else {
+            views.dayGroupHeader.setVisibility(View.GONE);
+        }
 
         // Update the expanded position if the rowIds match, in case ViewHolders were added/removed.
-        if (mCurrentlyExpandedRowId == rowId) {
+        // Then restore the state of the row on rebind.
+        if (mCurrentlyExpandedRowId == views.rowId) {
             mCurrentlyExpandedPosition = position;
         }
-        // Restore expansion state of the row on rebind. Inflate the actions ViewStub if required,
-        // and set its visibility state accordingly.
         views.showActions(mCurrentlyExpandedPosition == position, mOnReportButtonClickListener);
-
-        if (TextUtils.isEmpty(name)) {
-            details = new PhoneCallDetails(mContext, number, numberPresentation, formattedNumber,
-                    countryIso, geocode, callTypes, date, duration, accountHandle, features,
-                    dataUsage, transcription, isVoicemailNumber);
-        } else {
-            details = new PhoneCallDetails(mContext, number, numberPresentation, formattedNumber,
-                    countryIso, geocode, callTypes, date, duration, name, ntype, label, lookupUri,
-                    photoUri, sourceType, accountHandle, features, dataUsage, transcription,
-                    isVoicemailNumber);
-        }
-
-        mCallLogViewsHelper.setPhoneCallDetails(mContext, views, details);
+        views.updateCallButton();
 
         String nameForDefaultImage = null;
-        if (TextUtils.isEmpty(name)) {
+        if (TextUtils.isEmpty(info.name)) {
             nameForDefaultImage = details.displayNumber;
         } else {
-            nameForDefaultImage = name;
+            nameForDefaultImage = info.name;
         }
+        views.setPhoto(info.photoId, info.photoUri, info.lookupUri, nameForDefaultImage,
+                isVoicemailNumber, mContactInfoHelper.isBusiness(info.sourceType));
 
-        views.setPhoto(photoId, photoUri, lookupUri, nameForDefaultImage, isVoicemailNumber,
-                mContactInfoHelper.isBusiness(info.sourceType));
-
-        views.updateCallButton();
+        mCallLogViewsHelper.setPhoneCallDetails(mContext, views, details);
 
         // Listen for the first draw
         if (mViewTreeObserver == null) {
diff --git a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
index aa186eb..97fc324 100644
--- a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
+++ b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
@@ -139,35 +139,15 @@
             }
 
             // Read call log.
+            final String countryIso = cursor.getString(CallDetailQuery.COUNTRY_ISO_COLUMN_INDEX);
             final String number = cursor.getString(CallDetailQuery.NUMBER_COLUMN_INDEX);
             final int numberPresentation =
                     cursor.getInt(CallDetailQuery.NUMBER_PRESENTATION_COLUMN_INDEX);
-            final long date = cursor.getLong(CallDetailQuery.DATE_COLUMN_INDEX);
-            final long duration = cursor.getLong(CallDetailQuery.DURATION_COLUMN_INDEX);
-            final int callType = cursor.getInt(CallDetailQuery.CALL_TYPE_COLUMN_INDEX);
-            final String geocode = cursor.getString(CallDetailQuery.GEOCODED_LOCATION_COLUMN_INDEX);
-            final String transcription =
-                    cursor.getString(CallDetailQuery.TRANSCRIPTION_COLUMN_INDEX);
 
             final PhoneAccountHandle accountHandle = PhoneAccountUtils.getAccount(
                     cursor.getString(CallDetailQuery.ACCOUNT_COMPONENT_NAME),
                     cursor.getString(CallDetailQuery.ACCOUNT_ID));
 
-            String countryIso = cursor.getString(CallDetailQuery.COUNTRY_ISO_COLUMN_INDEX);
-            if (TextUtils.isEmpty(countryIso)) {
-                countryIso = GeoUtil.getCurrentCountryIso(context);
-            }
-
-            // Formatted phone number.
-            final CharSequence formattedNumber;
-            // Read contact specifics.
-            final CharSequence nameText;
-            final int numberType;
-            final CharSequence numberLabel;
-            final Uri photoUri;
-            final Uri lookupUri;
-            int sourceType;
-
             // If this is not a regular number, there is no point in looking it up in the contacts.
             ContactInfoHelper contactInfoHelper =
                     new ContactInfoHelper(context, GeoUtil.getCurrentCountryIso(context));
@@ -178,39 +158,36 @@
                     PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation)
                             && !isVoicemail;
             ContactInfo info = shouldLookupNumber
-                            ? contactInfoHelper.lookupNumber(number, countryIso) : null;
+                            ? contactInfoHelper.lookupNumber(number, countryIso)
+                            : ContactInfo.EMPTY;
+            PhoneCallDetails details = new PhoneCallDetails(
+                    context, number, numberPresentation, info.formattedNumber, isVoicemail);
 
-            if (info == null) {
-                formattedNumber = PhoneNumberDisplayUtil.getDisplayNumber(
-                        context, accountHandle, number, numberPresentation, null, isVoicemail);
-                nameText = "";
-                numberType = 0;
-                numberLabel = "";
-                photoUri = null;
-                lookupUri = null;
-                sourceType = 0;
-            } else {
-                formattedNumber = info.formattedNumber;
-                nameText = info.name;
-                numberType = info.type;
-                numberLabel = info.label;
-                photoUri = info.photoUri;
-                lookupUri = info.lookupUri;
-                sourceType = info.sourceType;
-            }
+            details.accountHandle = accountHandle;
+            details.contactUri = info.lookupUri;
+            details.name = info.name;
+            details.numberType = info.type;
+            details.numberLabel = info.label;
+            details.photoUri = info.photoUri;
+            details.sourceType = info.sourceType;
 
+            details.callTypes = new int[] {
+                    cursor.getInt(CallDetailQuery.CALL_TYPE_COLUMN_INDEX)
+            };
+            details.date = cursor.getLong(CallDetailQuery.DATE_COLUMN_INDEX);
+            details.duration = cursor.getLong(CallDetailQuery.DURATION_COLUMN_INDEX);
+            details.features = cursor.getInt(CallDetailQuery.FEATURES);
+            details.geocode = cursor.getString(CallDetailQuery.GEOCODED_LOCATION_COLUMN_INDEX);
+            details.transcription = cursor.getString(CallDetailQuery.TRANSCRIPTION_COLUMN_INDEX);
 
-            final int features = cursor.getInt(CallDetailQuery.FEATURES);
+            details.countryIso = !TextUtils.isEmpty(countryIso) ? countryIso
+                    : GeoUtil.getCurrentCountryIso(context);
 
-            Long dataUsage = null;
             if (!cursor.isNull(CallDetailQuery.DATA_USAGE)) {
-                dataUsage = cursor.getLong(CallDetailQuery.DATA_USAGE);
+                details.dataUsage = cursor.getLong(CallDetailQuery.DATA_USAGE);
             }
 
-            return new PhoneCallDetails(context, number, numberPresentation, formattedNumber,
-                    countryIso, geocode, new int[]{ callType }, date, duration, nameText,
-                    numberType, numberLabel, lookupUri, photoUri, sourceType, accountHandle,
-                    features, dataUsage, transcription, isVoicemail);
+            return details;
         } finally {
             if (cursor != null) {
                 cursor.close();
diff --git a/src/com/android/dialer/calllog/CallLogListItemHelper.java b/src/com/android/dialer/calllog/CallLogListItemHelper.java
index 4eb8797..6e3e8b5 100644
--- a/src/com/android/dialer/calllog/CallLogListItemHelper.java
+++ b/src/com/android/dialer/calllog/CallLogListItemHelper.java
@@ -186,7 +186,7 @@
             callDescription.append(mResources.getString(R.string.description_video_call));
         }
 
-        int stringID = getCallDescriptionStringID(details);
+        int stringID = getCallDescriptionStringID(details.callTypes);
         String accountLabel = PhoneAccountUtils.getAccountLabel(context, details.accountHandle);
 
         // Use chosen string resource to build up the message.
@@ -213,8 +213,8 @@
      * @param details Call details.
      * @return String resource ID to use.
      */
-    public int getCallDescriptionStringID(PhoneCallDetails details) {
-        int lastCallType = getLastCallType(details.callTypes);
+    public int getCallDescriptionStringID(int[] callTypes) {
+        int lastCallType = getLastCallType(callTypes);
         int stringID;
 
         if (lastCallType == Calls.VOICEMAIL_TYPE || lastCallType == Calls.MISSED_TYPE) {
diff --git a/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java b/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
index 7c2a966..942a73f 100644
--- a/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
+++ b/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
@@ -28,7 +28,6 @@
 import android.net.Uri;
 import android.provider.CallLog.Calls;
 import android.provider.ContactsContract.PhoneLookup;
-import android.telecom.PhoneAccountHandle;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -122,12 +121,8 @@
             // Check if we already know the name associated with this number.
             String name = names.get(newCall.number);
             if (name == null) {
-                PhoneAccountHandle accountHandle = PhoneAccountUtils.getAccount(
-                        newCall.accountComponentName,
-                        newCall.accountId);
                 name = PhoneNumberDisplayUtil.getDisplayName(
                         mContext,
-                        accountHandle,
                         newCall.number,
                         newCall.numberPresentation,
                         /* isVoicemail */ false).toString();
diff --git a/src/com/android/dialer/calllog/PhoneNumberDisplayUtil.java b/src/com/android/dialer/calllog/PhoneNumberDisplayUtil.java
index acfd32c..f80c2bc 100644
--- a/src/com/android/dialer/calllog/PhoneNumberDisplayUtil.java
+++ b/src/com/android/dialer/calllog/PhoneNumberDisplayUtil.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.provider.CallLog.Calls;
-import android.telecom.PhoneAccountHandle;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -35,7 +34,6 @@
      */
     /* package */ static CharSequence getDisplayName(
             Context context,
-            PhoneAccountHandle accountHandle,
             CharSequence number,
             int presentation,
             boolean isVoicemail) {
@@ -60,19 +58,16 @@
     /**
      * Returns the string to display for the given phone number.
      *
-     * @param accountHandle The handle for the account corresponding to the call
      * @param number the number to display
      * @param formattedNumber the formatted number if available, may be null
      */
     public static CharSequence getDisplayNumber(
             Context context,
-            PhoneAccountHandle accountHandle,
             CharSequence number,
             int presentation,
             CharSequence formattedNumber,
             boolean isVoicemail) {
-        final CharSequence displayName =
-                getDisplayName(context, accountHandle, number, presentation, isVoicemail);
+        final CharSequence displayName = getDisplayName(context, number, presentation, isVoicemail);
         if (!TextUtils.isEmpty(displayName)) {
             return displayName;
         }