Merge changes I4727645c,I13015ee6

* changes:
  Added CallLogConfig.
  Split CallDetailsActivity
diff --git a/java/com/android/dialer/app/DialtactsActivity.java b/java/com/android/dialer/app/DialtactsActivity.java
index 465c579..1a8e5cc 100644
--- a/java/com/android/dialer/app/DialtactsActivity.java
+++ b/java/com/android/dialer/app/DialtactsActivity.java
@@ -91,7 +91,7 @@
 import com.android.dialer.app.widget.ActionBarController;
 import com.android.dialer.app.widget.SearchEditTextLayout;
 import com.android.dialer.callcomposer.CallComposerActivity;
-import com.android.dialer.calldetails.CallDetailsActivity;
+import com.android.dialer.calldetails.OldCallDetailsActivity;
 import com.android.dialer.callintent.CallInitiationType;
 import com.android.dialer.callintent.CallIntentBuilder;
 import com.android.dialer.callintent.CallSpecificAppData;
@@ -838,8 +838,8 @@
     } else if (requestCode == ActivityRequestCodes.DIALTACTS_CALL_DETAILS) {
       if (resultCode == RESULT_OK
           && data != null
-          && data.getBooleanExtra(CallDetailsActivity.EXTRA_HAS_ENRICHED_CALL_DATA, false)) {
-        String number = data.getStringExtra(CallDetailsActivity.EXTRA_PHONE_NUMBER);
+          && data.getBooleanExtra(OldCallDetailsActivity.EXTRA_HAS_ENRICHED_CALL_DATA, false)) {
+        String number = data.getStringExtra(OldCallDetailsActivity.EXTRA_PHONE_NUMBER);
         int snackbarDurationMillis = 5_000;
         Snackbar.make(parentLayout, getString(R.string.ec_data_deleted), snackbarDurationMillis)
             .setAction(
diff --git a/java/com/android/dialer/app/calllog/CallLogActivity.java b/java/com/android/dialer/app/calllog/CallLogActivity.java
index 99e11b0..017c63a 100644
--- a/java/com/android/dialer/app/calllog/CallLogActivity.java
+++ b/java/com/android/dialer/app/calllog/CallLogActivity.java
@@ -33,7 +33,7 @@
 import com.android.contacts.common.list.ViewPagerTabs;
 import com.android.dialer.app.DialtactsActivity;
 import com.android.dialer.app.R;
-import com.android.dialer.calldetails.CallDetailsActivity;
+import com.android.dialer.calldetails.OldCallDetailsActivity;
 import com.android.dialer.common.Assert;
 import com.android.dialer.constants.ActivityRequestCodes;
 import com.android.dialer.database.CallLogQueryHandler;
@@ -282,8 +282,8 @@
     if (requestCode == ActivityRequestCodes.DIALTACTS_CALL_DETAILS) {
       if (resultCode == RESULT_OK
           && data != null
-          && data.getBooleanExtra(CallDetailsActivity.EXTRA_HAS_ENRICHED_CALL_DATA, false)) {
-        String number = data.getStringExtra(CallDetailsActivity.EXTRA_PHONE_NUMBER);
+          && data.getBooleanExtra(OldCallDetailsActivity.EXTRA_HAS_ENRICHED_CALL_DATA, false)) {
+        String number = data.getStringExtra(OldCallDetailsActivity.EXTRA_PHONE_NUMBER);
         Snackbar.make(findViewById(R.id.calllog_frame), getString(R.string.ec_data_deleted), 5_000)
             .setAction(
                 R.string.view_conversation,
diff --git a/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
index 4c187fa..94bd933 100644
--- a/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
+++ b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
@@ -63,8 +63,8 @@
 import com.android.dialer.blocking.FilteredNumberCompat;
 import com.android.dialer.blocking.FilteredNumbersUtil;
 import com.android.dialer.callcomposer.CallComposerActivity;
-import com.android.dialer.calldetails.CallDetailsActivity;
 import com.android.dialer.calldetails.CallDetailsEntries;
+import com.android.dialer.calldetails.OldCallDetailsActivity;
 import com.android.dialer.callintent.CallIntentBuilder;
 import com.android.dialer.calllogutils.CallbackActionHelper.CallbackAction;
 import com.android.dialer.clipboard.ClipboardUtils;
@@ -1037,7 +1037,7 @@
     String packageName = intent.getPackage();
     if (DuoConstants.PACKAGE_NAME.equals(packageName)) {
       startDuoActivity(intent);
-    } else if (CallDetailsActivity.isLaunchIntent(intent)) {
+    } else if (OldCallDetailsActivity.isLaunchIntent(intent)) {
       PerformanceReport.recordClick(UiAction.Type.OPEN_CALL_DETAIL);
       ((Activity) context)
           .startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_CALL_DETAILS);
diff --git a/java/com/android/dialer/app/calllog/IntentProvider.java b/java/com/android/dialer/app/calllog/IntentProvider.java
index 3a07a6f..a794c62 100644
--- a/java/com/android/dialer/app/calllog/IntentProvider.java
+++ b/java/com/android/dialer/app/calllog/IntentProvider.java
@@ -27,8 +27,8 @@
 import android.telephony.TelephonyManager;
 import com.android.contacts.common.model.Contact;
 import com.android.contacts.common.model.ContactLoader;
-import com.android.dialer.calldetails.CallDetailsActivity;
 import com.android.dialer.calldetails.CallDetailsEntries;
+import com.android.dialer.calldetails.OldCallDetailsActivity;
 import com.android.dialer.callintent.CallInitiationType;
 import com.android.dialer.callintent.CallIntentBuilder;
 import com.android.dialer.dialercontact.DialerContact;
@@ -184,7 +184,7 @@
     return new IntentProvider() {
       @Override
       public Intent getIntent(Context context) {
-        return CallDetailsActivity.newInstance(
+        return OldCallDetailsActivity.newInstance(
             context, callDetailsEntries, contact, canReportCallerId, canSupportAssistedDialing);
       }
     };
diff --git a/java/com/android/dialer/calldetails/AndroidManifest.xml b/java/com/android/dialer/calldetails/AndroidManifest.xml
index 9cf656e..9ef05a3 100644
--- a/java/com/android/dialer/calldetails/AndroidManifest.xml
+++ b/java/com/android/dialer/calldetails/AndroidManifest.xml
@@ -20,8 +20,14 @@
     <activity
       android:label="@string/call_details"
       android:exported="false"
-      android:name="com.android.dialer.calldetails.CallDetailsActivity"
+      android:name="com.android.dialer.calldetails.OldCallDetailsActivity"
       android:theme="@style/DialerThemeBase.NoActionBar">
     </activity>
+    <activity
+        android:label="@string/call_details"
+        android:exported="false"
+        android:name="com.android.dialer.calldetails.CallDetailsActivity"
+        android:theme="@style/DialerThemeBase.NoActionBar">
+    </activity>
   </application>
 </manifest>
diff --git a/java/com/android/dialer/calldetails/CallDetailsActivity.java b/java/com/android/dialer/calldetails/CallDetailsActivity.java
index ec124df..672043b 100644
--- a/java/com/android/dialer/calldetails/CallDetailsActivity.java
+++ b/java/com/android/dialer/calldetails/CallDetailsActivity.java
@@ -16,122 +16,42 @@
 
 package com.android.dialer.calldetails;
 
-import android.Manifest.permission;
-import android.annotation.SuppressLint;
-import android.app.Activity;
 import android.app.LoaderManager.LoaderCallbacks;
-import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.Loader;
 import android.database.Cursor;
-import android.os.AsyncTask;
 import android.os.Bundle;
-import android.provider.CallLog;
-import android.provider.CallLog.Calls;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresPermission;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.Toolbar;
-import android.view.View;
-import android.widget.Toast;
 import com.android.dialer.CoalescedIds;
-import com.android.dialer.assisteddialing.ui.AssistedDialingSettingActivity;
-import com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry;
-import com.android.dialer.callintent.CallInitiationType;
-import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.calldetails.CallDetailsFooterViewHolder.DeleteCallDetailsListener;
+import com.android.dialer.calldetails.CallDetailsFooterViewHolder.ReportCallIdListener;
+import com.android.dialer.calldetails.CallDetailsHeaderViewHolder.CallDetailsHeaderListener;
 import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog;
 import com.android.dialer.common.Assert;
-import com.android.dialer.common.LogUtil;
-import com.android.dialer.common.concurrent.AsyncTaskExecutors;
-import com.android.dialer.common.concurrent.DialerExecutor.FailureListener;
-import com.android.dialer.common.concurrent.DialerExecutor.SuccessListener;
-import com.android.dialer.common.concurrent.DialerExecutor.Worker;
-import com.android.dialer.common.concurrent.DialerExecutorComponent;
-import com.android.dialer.constants.ActivityRequestCodes;
 import com.android.dialer.dialercontact.DialerContact;
-import com.android.dialer.duo.Duo;
-import com.android.dialer.duo.DuoComponent;
 import com.android.dialer.enrichedcall.EnrichedCallComponent;
-import com.android.dialer.enrichedcall.EnrichedCallManager;
-import com.android.dialer.enrichedcall.historyquery.proto.HistoryResult;
-import com.android.dialer.logging.DialerImpression;
-import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.UiAction;
-import com.android.dialer.performancereport.PerformanceReport;
-import com.android.dialer.postcall.PostCall;
-import com.android.dialer.precall.PreCall;
 import com.android.dialer.protos.ProtoParsers;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.i18n.phonenumbers.NumberParseException;
-import com.google.i18n.phonenumbers.PhoneNumberUtil;
-import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
-import java.lang.ref.WeakReference;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
 
-/** Displays the details of a specific call log entry. */
-public class CallDetailsActivity extends AppCompatActivity {
-  private static final int CALL_DETAILS_LOADER_ID = 0;
-
-  public static final String EXTRA_PHONE_NUMBER = "phone_number";
-  public static final String EXTRA_HAS_ENRICHED_CALL_DATA = "has_enriched_call_data";
-  public static final String EXTRA_CALL_DETAILS_ENTRIES = "call_details_entries";
+/**
+ * Displays the details of a specific call log entry.
+ *
+ * <p>This activity is for the new call log.
+ *
+ * <p>See {@link CallDetailsAdapterCommon} for logic shared between this activity and the one for
+ * the old call log.
+ */
+public final class CallDetailsActivity extends CallDetailsActivityCommon {
   public static final String EXTRA_COALESCED_CALL_LOG_IDS = "coalesced_call_log_ids";
   public static final String EXTRA_CONTACT = "contact";
-  public static final String EXTRA_CAN_REPORT_CALLER_ID = "can_report_caller_id";
-  public static final String EXTRA_CAN_SUPPORT_ASSISTED_DIALING = "can_support_assisted_dialing";
 
-  private final CallDetailsHeaderViewHolder.CallDetailsHeaderListener callDetailsHeaderListener =
-      new CallDetailsHeaderListener(this);
-  private final CallDetailsFooterViewHolder.DeleteCallDetailsListener deleteCallDetailsListener =
-      new DeleteCallDetailsListener(this);
-  private final CallDetailsFooterViewHolder.ReportCallIdListener reportCallIdListener =
-      new ReportCallIdListener(this);
-  private final EnrichedCallManager.HistoricalDataChangedListener
-      enrichedCallHistoricalDataChangedListener =
-          new EnrichedCallHistoricalDataChangedListener(this);
+  private static final int CALL_DETAILS_LOADER_ID = 0;
 
-  private CallDetailsEntries entries;
+  /** IDs of call log entries, used to retrieve them from the annotated call log. */
+  private CoalescedIds coalescedCallLogIds;
+
   private DialerContact contact;
-  private CallDetailsAdapter adapter;
 
-  // This will be present only when the activity is launched from the new call log UI, i.e., a list
-  // of coalesced annotated call log IDs is included in the intent.
-  private Optional<CoalescedIds> coalescedCallLogIds = Optional.absent();
-
-  public static boolean isLaunchIntent(Intent intent) {
-    return intent.getComponent() != null
-        && CallDetailsActivity.class.getName().equals(intent.getComponent().getClassName());
-  }
-
-  /**
-   * Returns an {@link Intent} for launching the {@link CallDetailsActivity} from the old call log
-   * UI.
-   */
-  public static Intent newInstance(
-      Context context,
-      CallDetailsEntries details,
-      DialerContact contact,
-      boolean canReportCallerId,
-      boolean canSupportAssistedDialing) {
-    Intent intent = new Intent(context, CallDetailsActivity.class);
-    ProtoParsers.put(intent, EXTRA_CONTACT, Assert.isNotNull(contact));
-    ProtoParsers.put(intent, EXTRA_CALL_DETAILS_ENTRIES, Assert.isNotNull(details));
-    intent.putExtra(EXTRA_CAN_REPORT_CALLER_ID, canReportCallerId);
-    intent.putExtra(EXTRA_CAN_SUPPORT_ASSISTED_DIALING, canSupportAssistedDialing);
-    return intent;
-  }
-
-  /**
-   * Returns an {@link Intent} for launching the {@link CallDetailsActivity} from the new call log
-   * UI.
-   */
+  /** Returns an {@link Intent} to launch this activity. */
   public static Intent newInstance(
       Context context,
       CoalescedIds coalescedAnnotatedCallLogIds,
@@ -148,99 +68,39 @@
   }
 
   @Override
-  protected void onCreate(Bundle savedInstanceState) {
-    super.onCreate(savedInstanceState);
-    setContentView(R.layout.call_details_activity);
-    Toolbar toolbar = findViewById(R.id.toolbar);
-    toolbar.setTitle(R.string.call_details);
-    toolbar.setNavigationOnClickListener(
-        v -> {
-          PerformanceReport.recordClick(UiAction.Type.CLOSE_CALL_DETAIL_WITH_CANCEL_BUTTON);
-          finish();
-        });
-    onHandleIntent(getIntent());
-  }
-
-  @Override
-  protected void onResume() {
-    super.onResume();
-
-    // Some calls may not be recorded (eg. from quick contact),
-    // so we should restart recording after these calls. (Recorded call is stopped)
-    PostCall.restartPerformanceRecordingIfARecentCallExist(this);
-    if (!PerformanceReport.isRecording()) {
-      PerformanceReport.startRecording();
-    }
-
-    PostCall.promptUserForMessageIfNecessary(this, findViewById(R.id.recycler_view));
-
-    EnrichedCallComponent.get(this)
-        .getEnrichedCallManager()
-        .registerHistoricalDataChangedListener(enrichedCallHistoricalDataChangedListener);
-    EnrichedCallComponent.get(this)
-        .getEnrichedCallManager()
-        .requestAllHistoricalData(contact.getNumber(), entries);
-  }
-
-  @Override
-  protected void onPause() {
-    super.onPause();
-
-    EnrichedCallComponent.get(this)
-        .getEnrichedCallManager()
-        .unregisterHistoricalDataChangedListener(enrichedCallHistoricalDataChangedListener);
-  }
-
-  @Override
-  protected void onNewIntent(Intent intent) {
-    super.onNewIntent(intent);
-    onHandleIntent(intent);
-  }
-
-  private void onHandleIntent(Intent intent) {
-    boolean hasCallDetailsEntries = intent.hasExtra(EXTRA_CALL_DETAILS_ENTRIES);
-    boolean hasCoalescedCallLogIds = intent.hasExtra(EXTRA_COALESCED_CALL_LOG_IDS);
-    Assert.checkArgument(
-        (hasCallDetailsEntries && !hasCoalescedCallLogIds)
-            || (!hasCallDetailsEntries && hasCoalescedCallLogIds),
-        "One and only one of EXTRA_CALL_DETAILS_ENTRIES and EXTRA_COALESCED_CALL_LOG_IDS "
-            + "can be included in the intent.");
+  protected void handleIntent(Intent intent) {
+    Assert.checkArgument(intent.hasExtra(EXTRA_COALESCED_CALL_LOG_IDS));
+    Assert.checkArgument(intent.hasExtra(EXTRA_CAN_REPORT_CALLER_ID));
+    Assert.checkArgument(intent.hasExtra(EXTRA_CAN_SUPPORT_ASSISTED_DIALING));
 
     contact = ProtoParsers.getTrusted(intent, EXTRA_CONTACT, DialerContact.getDefaultInstance());
-    if (hasCallDetailsEntries) {
-      entries =
-          ProtoParsers.getTrusted(
-              intent, EXTRA_CALL_DETAILS_ENTRIES, CallDetailsEntries.getDefaultInstance());
-    } else {
-      entries = CallDetailsEntries.getDefaultInstance();
-      coalescedCallLogIds =
-          Optional.of(
-              ProtoParsers.getTrusted(
-                  intent, EXTRA_COALESCED_CALL_LOG_IDS, CoalescedIds.getDefaultInstance()));
-      getLoaderManager()
-          .initLoader(
-              CALL_DETAILS_LOADER_ID, /* args = */ null, new CallDetailsLoaderCallbacks(this));
-    }
+    setCallDetailsEntries(CallDetailsEntries.getDefaultInstance());
+    coalescedCallLogIds =
+        ProtoParsers.getTrusted(
+            intent, EXTRA_COALESCED_CALL_LOG_IDS, CoalescedIds.getDefaultInstance());
 
-    adapter =
-        new CallDetailsAdapter(
-            this /* context */,
-            contact,
-            entries.getEntriesList(),
-            callDetailsHeaderListener,
-            reportCallIdListener,
-            deleteCallDetailsListener);
-
-    RecyclerView recyclerView = findViewById(R.id.recycler_view);
-    recyclerView.setLayoutManager(new LinearLayoutManager(this));
-    recyclerView.setAdapter(adapter);
-    PerformanceReport.logOnScrollStateChange(recyclerView);
+    getLoaderManager()
+        .initLoader(
+            CALL_DETAILS_LOADER_ID, /* args = */ null, new CallDetailsLoaderCallbacks(this));
   }
 
   @Override
-  public void onBackPressed() {
-    PerformanceReport.recordClick(UiAction.Type.PRESS_ANDROID_BACK_BUTTON);
-    super.onBackPressed();
+  protected CallDetailsAdapterCommon createAdapter(
+      CallDetailsHeaderListener callDetailsHeaderListener,
+      ReportCallIdListener reportCallIdListener,
+      DeleteCallDetailsListener deleteCallDetailsListener) {
+    return new CallDetailsAdapter(
+        this,
+        contact,
+        getCallDetailsEntries(),
+        callDetailsHeaderListener,
+        reportCallIdListener,
+        deleteCallDetailsListener);
+  }
+
+  @Override
+  protected String getNumber() {
+    return contact.getNumber();
   }
 
   /**
@@ -256,9 +116,7 @@
 
     @Override
     public Loader<Cursor> onCreateLoader(int id, Bundle args) {
-      Assert.checkState(activity.coalescedCallLogIds.isPresent());
-
-      return new CallDetailsCursorLoader(activity, activity.coalescedCallLogIds.get());
+      return new CallDetailsCursorLoader(activity, Assert.isNotNull(activity.coalescedCallLogIds));
     }
 
     @Override
@@ -272,290 +130,11 @@
     }
 
     private void updateCallDetailsEntries(CallDetailsEntries newEntries) {
-      activity.entries = newEntries;
-      activity.adapter.updateCallDetailsEntries(newEntries.getEntriesList());
+      activity.setCallDetailsEntries(newEntries);
+      activity.getAdapter().updateCallDetailsEntries(newEntries);
       EnrichedCallComponent.get(activity)
           .getEnrichedCallManager()
-          .requestAllHistoricalData(activity.contact.getNumber(), newEntries);
-    }
-  }
-
-  /** Delete specified calls from the call log. */
-  private static class DeleteCallsTask extends AsyncTask<Void, Void, Void> {
-    // Use a weak reference to hold the Activity so that there is no memory leak.
-    private final WeakReference<Activity> activityWeakReference;
-
-    private final DialerContact contact;
-    private final CallDetailsEntries callDetailsEntries;
-    private final String callIds;
-
-    DeleteCallsTask(
-        Activity activity, DialerContact contact, CallDetailsEntries callDetailsEntries) {
-      this.activityWeakReference = new WeakReference<>(activity);
-      this.contact = contact;
-      this.callDetailsEntries = callDetailsEntries;
-
-      StringBuilder callIds = new StringBuilder();
-      for (CallDetailsEntry entry : callDetailsEntries.getEntriesList()) {
-        if (callIds.length() != 0) {
-          callIds.append(",");
-        }
-        callIds.append(entry.getCallId());
-      }
-      this.callIds = callIds.toString();
-    }
-
-    @Override
-    // Suppress the lint check here as the user will not be able to see call log entries if
-    // permission.WRITE_CALL_LOG is not granted.
-    @SuppressLint("MissingPermission")
-    @RequiresPermission(value = permission.WRITE_CALL_LOG)
-    protected Void doInBackground(Void... params) {
-      Activity activity = activityWeakReference.get();
-      if (activity == null) {
-        return null;
-      }
-
-      activity
-          .getContentResolver()
-          .delete(
-              Calls.CONTENT_URI,
-              CallLog.Calls._ID + " IN (" + callIds + ")" /* where */,
-              null /* selectionArgs */);
-      return null;
-    }
-
-    @Override
-    public void onPostExecute(Void result) {
-      Activity activity = activityWeakReference.get();
-      if (activity == null) {
-        return;
-      }
-
-      Intent data = new Intent();
-      data.putExtra(EXTRA_PHONE_NUMBER, contact.getNumber());
-      for (CallDetailsEntry entry : callDetailsEntries.getEntriesList()) {
-        if (entry.getHistoryResultsCount() > 0) {
-          data.putExtra(EXTRA_HAS_ENRICHED_CALL_DATA, true);
-          break;
-        }
-      }
-
-      activity.setResult(RESULT_OK, data);
-      activity.finish();
-    }
-  }
-
-  private static final class CallDetailsHeaderListener
-      implements CallDetailsHeaderViewHolder.CallDetailsHeaderListener {
-    private final WeakReference<CallDetailsActivity> activityWeakReference;
-
-    CallDetailsHeaderListener(CallDetailsActivity activity) {
-      this.activityWeakReference = new WeakReference<>(activity);
-    }
-
-    @Override
-    public void placeImsVideoCall(String phoneNumber) {
-      Logger.get(getActivity())
-          .logImpression(DialerImpression.Type.CALL_DETAILS_IMS_VIDEO_CALL_BACK);
-      PreCall.start(
-          getActivity(),
-          new CallIntentBuilder(phoneNumber, CallInitiationType.Type.CALL_DETAILS)
-              .setIsVideoCall(true));
-    }
-
-    @Override
-    public void placeDuoVideoCall(String phoneNumber) {
-      Logger.get(getActivity())
-          .logImpression(DialerImpression.Type.CALL_DETAILS_LIGHTBRINGER_CALL_BACK);
-      Duo duo = DuoComponent.get(getActivity()).getDuo();
-      if (!duo.isReachable(getActivity(), phoneNumber)) {
-        placeImsVideoCall(phoneNumber);
-        return;
-      }
-
-      try {
-        getActivity()
-            .startActivityForResult(
-                duo.getIntent(getActivity(), phoneNumber), ActivityRequestCodes.DIALTACTS_DUO);
-      } catch (ActivityNotFoundException e) {
-        Toast.makeText(getActivity(), R.string.activity_not_available, Toast.LENGTH_SHORT).show();
-      }
-    }
-
-    @Override
-    public void placeVoiceCall(String phoneNumber, String postDialDigits) {
-      Logger.get(getActivity()).logImpression(DialerImpression.Type.CALL_DETAILS_VOICE_CALL_BACK);
-
-      boolean canSupportedAssistedDialing =
-          getActivity()
-              .getIntent()
-              .getExtras()
-              .getBoolean(EXTRA_CAN_SUPPORT_ASSISTED_DIALING, false);
-      CallIntentBuilder callIntentBuilder =
-          new CallIntentBuilder(phoneNumber + postDialDigits, CallInitiationType.Type.CALL_DETAILS);
-      if (canSupportedAssistedDialing) {
-        callIntentBuilder.setAllowAssistedDial(true);
-      }
-
-      PreCall.start(getActivity(), callIntentBuilder);
-    }
-
-    private CallDetailsActivity getActivity() {
-      return Preconditions.checkNotNull(activityWeakReference.get());
-    }
-
-    @Override
-    public void openAssistedDialingSettings(View unused) {
-        Intent intent = new Intent(getActivity(), AssistedDialingSettingActivity.class);
-        getActivity().startActivity(intent);
-    }
-
-    @Override
-    public void createAssistedDialerNumberParserTask(
-        AssistedDialingNumberParseWorker worker,
-        SuccessListener<Integer> successListener,
-        FailureListener failureListener) {
-      DialerExecutorComponent.get(getActivity().getApplicationContext())
-          .dialerExecutorFactory()
-          .createUiTaskBuilder(
-              getActivity().getFragmentManager(),
-              "CallDetailsActivity.createAssistedDialerNumberParserTask",
-              new AssistedDialingNumberParseWorker())
-          .onSuccess(successListener)
-          .onFailure(failureListener)
-          .build()
-          .executeParallel(getActivity().contact.getNumber());
-    }
-  }
-
-  static class AssistedDialingNumberParseWorker implements Worker<String, Integer> {
-
-    @Override
-    public Integer doInBackground(@NonNull String phoneNumber) {
-      PhoneNumber parsedNumber = null;
-      try {
-        parsedNumber = PhoneNumberUtil.getInstance().parse(phoneNumber, null);
-      } catch (NumberParseException e) {
-        LogUtil.w(
-            "AssistedDialingNumberParseWorker.doInBackground",
-            "couldn't parse phone number: " + LogUtil.sanitizePii(phoneNumber),
-            e);
-        return 0;
-      }
-      return parsedNumber.getCountryCode();
-    }
-  }
-
-  private static final class DeleteCallDetailsListener
-      implements CallDetailsFooterViewHolder.DeleteCallDetailsListener {
-    private static final String ASYNC_TASK_ID = "task_delete";
-
-    private final WeakReference<CallDetailsActivity> activityWeakReference;
-
-    DeleteCallDetailsListener(CallDetailsActivity activity) {
-      this.activityWeakReference = new WeakReference<>(activity);
-    }
-
-    @Override
-    public void delete() {
-      AsyncTaskExecutors.createAsyncTaskExecutor()
-          .submit(
-              ASYNC_TASK_ID,
-              new DeleteCallsTask(getActivity(), getActivity().contact, getActivity().entries));
-    }
-
-    private CallDetailsActivity getActivity() {
-      return Preconditions.checkNotNull(activityWeakReference.get());
-    }
-  }
-
-  private static final class ReportCallIdListener
-      implements CallDetailsFooterViewHolder.ReportCallIdListener {
-    private final WeakReference<Activity> activityWeakReference;
-
-    ReportCallIdListener(Activity activity) {
-      this.activityWeakReference = new WeakReference<>(activity);
-    }
-
-    @Override
-    public void reportCallId(String number) {
-      ReportDialogFragment.newInstance(number)
-          .show(getActivity().getFragmentManager(), null /* tag */);
-    }
-
-    @Override
-    public boolean canReportCallerId(String number) {
-      return getActivity().getIntent().getExtras().getBoolean(EXTRA_CAN_REPORT_CALLER_ID, false);
-    }
-
-    private Activity getActivity() {
-      return Preconditions.checkNotNull(activityWeakReference.get());
-    }
-  }
-
-  private static final class EnrichedCallHistoricalDataChangedListener
-      implements EnrichedCallManager.HistoricalDataChangedListener {
-    private final WeakReference<CallDetailsActivity> activityWeakReference;
-
-    EnrichedCallHistoricalDataChangedListener(CallDetailsActivity activity) {
-      this.activityWeakReference = new WeakReference<>(activity);
-    }
-
-    @Override
-    public void onHistoricalDataChanged() {
-      CallDetailsActivity activity = getActivity();
-      Map<CallDetailsEntry, List<HistoryResult>> mappedResults =
-          getAllHistoricalData(activity.contact.getNumber(), activity.entries);
-
-      activity.adapter.updateCallDetailsEntries(
-          generateAndMapNewCallDetailsEntriesHistoryResults(
-                  activity.contact.getNumber(), activity.entries, mappedResults)
-              .getEntriesList());
-    }
-
-    private CallDetailsActivity getActivity() {
-      return Preconditions.checkNotNull(activityWeakReference.get());
-    }
-
-    @NonNull
-    private Map<CallDetailsEntry, List<HistoryResult>> getAllHistoricalData(
-        @Nullable String number, @NonNull CallDetailsEntries entries) {
-      if (number == null) {
-        return Collections.emptyMap();
-      }
-
-      Map<CallDetailsEntry, List<HistoryResult>> historicalData =
-          EnrichedCallComponent.get(getActivity())
-              .getEnrichedCallManager()
-              .getAllHistoricalData(number, entries);
-      if (historicalData == null) {
-        return Collections.emptyMap();
-      }
-      return historicalData;
-    }
-
-    private static CallDetailsEntries generateAndMapNewCallDetailsEntriesHistoryResults(
-        @Nullable String number,
-        @NonNull CallDetailsEntries callDetailsEntries,
-        @NonNull Map<CallDetailsEntry, List<HistoryResult>> mappedResults) {
-      if (number == null) {
-        return callDetailsEntries;
-      }
-      CallDetailsEntries.Builder mutableCallDetailsEntries = CallDetailsEntries.newBuilder();
-      for (CallDetailsEntry entry : callDetailsEntries.getEntriesList()) {
-        CallDetailsEntry.Builder newEntry = CallDetailsEntry.newBuilder().mergeFrom(entry);
-        List<HistoryResult> results = mappedResults.get(entry);
-        if (results != null) {
-          newEntry.addAllHistoryResults(mappedResults.get(entry));
-          LogUtil.v(
-              "CallDetailsActivity.generateAndMapNewCallDetailsEntriesHistoryResults",
-              "mapped %d results",
-              newEntry.getHistoryResultsList().size());
-        }
-        mutableCallDetailsEntries.addEntries(newEntry.build());
-      }
-      return mutableCallDetailsEntries.build();
+          .requestAllHistoricalData(activity.getNumber(), newEntries);
     }
   }
 }
diff --git a/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java b/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java
new file mode 100644
index 0000000..45200b9
--- /dev/null
+++ b/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2017 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.dialer.calldetails;
+
+import android.Manifest.permission;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.support.annotation.CallSuper;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresPermission;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.Toolbar;
+import android.view.View;
+import android.widget.Toast;
+import com.android.dialer.assisteddialing.ui.AssistedDialingSettingActivity;
+import com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry;
+import com.android.dialer.callintent.CallInitiationType;
+import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.DialerExecutor.FailureListener;
+import com.android.dialer.common.concurrent.DialerExecutor.SuccessListener;
+import com.android.dialer.common.concurrent.DialerExecutor.Worker;
+import com.android.dialer.common.concurrent.DialerExecutorComponent;
+import com.android.dialer.common.database.Selection;
+import com.android.dialer.constants.ActivityRequestCodes;
+import com.android.dialer.duo.Duo;
+import com.android.dialer.duo.DuoComponent;
+import com.android.dialer.enrichedcall.EnrichedCallComponent;
+import com.android.dialer.enrichedcall.EnrichedCallManager;
+import com.android.dialer.enrichedcall.historyquery.proto.HistoryResult;
+import com.android.dialer.logging.DialerImpression;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.UiAction;
+import com.android.dialer.performancereport.PerformanceReport;
+import com.android.dialer.postcall.PostCall;
+import com.android.dialer.precall.PreCall;
+import com.google.common.base.Preconditions;
+import com.google.i18n.phonenumbers.NumberParseException;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Contains common logic shared between {@link OldCallDetailsActivity} and {@link
+ * CallDetailsActivity}.
+ */
+abstract class CallDetailsActivityCommon extends AppCompatActivity {
+
+  public static final String EXTRA_PHONE_NUMBER = "phone_number";
+  public static final String EXTRA_HAS_ENRICHED_CALL_DATA = "has_enriched_call_data";
+  public static final String EXTRA_CAN_REPORT_CALLER_ID = "can_report_caller_id";
+  public static final String EXTRA_CAN_SUPPORT_ASSISTED_DIALING = "can_support_assisted_dialing";
+
+  private final CallDetailsHeaderViewHolder.CallDetailsHeaderListener callDetailsHeaderListener =
+      new CallDetailsHeaderListener(this);
+  private final CallDetailsFooterViewHolder.DeleteCallDetailsListener deleteCallDetailsListener =
+      new DeleteCallDetailsListener(this);
+  private final CallDetailsFooterViewHolder.ReportCallIdListener reportCallIdListener =
+      new ReportCallIdListener(this);
+  private final EnrichedCallManager.HistoricalDataChangedListener
+      enrichedCallHistoricalDataChangedListener =
+          new EnrichedCallHistoricalDataChangedListener(this);
+
+  private CallDetailsAdapterCommon adapter;
+  private CallDetailsEntries callDetailsEntries;
+
+  /**
+   * Handles the intent that launches {@link OldCallDetailsActivity} or {@link CallDetailsActivity},
+   * e.g., extract data from intent extras, start loading data, etc.
+   */
+  protected abstract void handleIntent(Intent intent);
+
+  /** Creates an adapter for {@link OldCallDetailsActivity} or {@link CallDetailsActivity}. */
+  protected abstract CallDetailsAdapterCommon createAdapter(
+      CallDetailsHeaderViewHolder.CallDetailsHeaderListener callDetailsHeaderListener,
+      CallDetailsFooterViewHolder.ReportCallIdListener reportCallIdListener,
+      CallDetailsFooterViewHolder.DeleteCallDetailsListener deleteCallDetailsListener);
+
+  /** Returns the phone number of the call details. */
+  protected abstract String getNumber();
+
+  @Override
+  @CallSuper
+  protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.call_details_activity);
+    Toolbar toolbar = findViewById(R.id.toolbar);
+    toolbar.setTitle(R.string.call_details);
+    toolbar.setNavigationOnClickListener(
+        v -> {
+          PerformanceReport.recordClick(UiAction.Type.CLOSE_CALL_DETAIL_WITH_CANCEL_BUTTON);
+          finish();
+        });
+    handleIntent(getIntent());
+    setupRecyclerViewForEntries();
+  }
+
+  @Override
+  @CallSuper
+  protected void onResume() {
+    super.onResume();
+
+    // Some calls may not be recorded (eg. from quick contact),
+    // so we should restart recording after these calls. (Recorded call is stopped)
+    PostCall.restartPerformanceRecordingIfARecentCallExist(this);
+    if (!PerformanceReport.isRecording()) {
+      PerformanceReport.startRecording();
+    }
+
+    PostCall.promptUserForMessageIfNecessary(this, findViewById(R.id.recycler_view));
+
+    EnrichedCallComponent.get(this)
+        .getEnrichedCallManager()
+        .registerHistoricalDataChangedListener(enrichedCallHistoricalDataChangedListener);
+    EnrichedCallComponent.get(this)
+        .getEnrichedCallManager()
+        .requestAllHistoricalData(getNumber(), callDetailsEntries);
+  }
+
+  @Override
+  @CallSuper
+  protected void onPause() {
+    super.onPause();
+
+    EnrichedCallComponent.get(this)
+        .getEnrichedCallManager()
+        .unregisterHistoricalDataChangedListener(enrichedCallHistoricalDataChangedListener);
+  }
+
+  @Override
+  @CallSuper
+  protected void onNewIntent(Intent intent) {
+    super.onNewIntent(intent);
+
+    handleIntent(intent);
+    setupRecyclerViewForEntries();
+  }
+
+  private void setupRecyclerViewForEntries() {
+    adapter =
+        createAdapter(callDetailsHeaderListener, reportCallIdListener, deleteCallDetailsListener);
+
+    RecyclerView recyclerView = findViewById(R.id.recycler_view);
+    recyclerView.setLayoutManager(new LinearLayoutManager(this));
+    recyclerView.setAdapter(adapter);
+    PerformanceReport.logOnScrollStateChange(recyclerView);
+  }
+
+  final CallDetailsAdapterCommon getAdapter() {
+    return adapter;
+  }
+
+  @Override
+  @CallSuper
+  public void onBackPressed() {
+    PerformanceReport.recordClick(UiAction.Type.PRESS_ANDROID_BACK_BUTTON);
+    super.onBackPressed();
+  }
+
+  protected final void setCallDetailsEntries(CallDetailsEntries entries) {
+    this.callDetailsEntries = entries;
+  }
+
+  protected final CallDetailsEntries getCallDetailsEntries() {
+    return callDetailsEntries;
+  }
+
+  /** A {@link Worker} that deletes specified entries from the call log. */
+  private static final class DeleteCallsWorker implements Worker<CallDetailsEntries, Void> {
+    // Use a weak reference to hold the Activity so that there is no memory leak.
+    private final WeakReference<Context> contextWeakReference;
+
+    DeleteCallsWorker(Context context) {
+      this.contextWeakReference = new WeakReference<>(context);
+    }
+
+    @Override
+    // Suppress the lint check here as the user will not be able to see call log entries if
+    // permission.WRITE_CALL_LOG is not granted.
+    @SuppressLint("MissingPermission")
+    @RequiresPermission(value = permission.WRITE_CALL_LOG)
+    public Void doInBackground(CallDetailsEntries callDetailsEntries) {
+      Context context = contextWeakReference.get();
+      if (context == null) {
+        return null;
+      }
+
+      Selection selection =
+          Selection.builder()
+              .and(Selection.column(CallLog.Calls._ID).in(getCallLogIdList(callDetailsEntries)))
+              .build();
+
+      context
+          .getContentResolver()
+          .delete(Calls.CONTENT_URI, selection.getSelection(), selection.getSelectionArgs());
+      return null;
+    }
+
+    private static List<String> getCallLogIdList(CallDetailsEntries callDetailsEntries) {
+      Assert.checkArgument(callDetailsEntries.getEntriesCount() > 0);
+
+      List<String> idStrings = new ArrayList<>(callDetailsEntries.getEntriesCount());
+
+      for (CallDetailsEntry entry : callDetailsEntries.getEntriesList()) {
+        idStrings.add(String.valueOf(entry.getCallId()));
+      }
+
+      return idStrings;
+    }
+  }
+
+  private static final class CallDetailsHeaderListener
+      implements CallDetailsHeaderViewHolder.CallDetailsHeaderListener {
+    private final WeakReference<CallDetailsActivityCommon> activityWeakReference;
+
+    CallDetailsHeaderListener(CallDetailsActivityCommon activity) {
+      this.activityWeakReference = new WeakReference<>(activity);
+    }
+
+    @Override
+    public void placeImsVideoCall(String phoneNumber) {
+      Logger.get(getActivity())
+          .logImpression(DialerImpression.Type.CALL_DETAILS_IMS_VIDEO_CALL_BACK);
+      PreCall.start(
+          getActivity(),
+          new CallIntentBuilder(phoneNumber, CallInitiationType.Type.CALL_DETAILS)
+              .setIsVideoCall(true));
+    }
+
+    @Override
+    public void placeDuoVideoCall(String phoneNumber) {
+      Logger.get(getActivity())
+          .logImpression(DialerImpression.Type.CALL_DETAILS_LIGHTBRINGER_CALL_BACK);
+      Duo duo = DuoComponent.get(getActivity()).getDuo();
+      if (!duo.isReachable(getActivity(), phoneNumber)) {
+        placeImsVideoCall(phoneNumber);
+        return;
+      }
+
+      try {
+        getActivity()
+            .startActivityForResult(
+                duo.getIntent(getActivity(), phoneNumber), ActivityRequestCodes.DIALTACTS_DUO);
+      } catch (ActivityNotFoundException e) {
+        Toast.makeText(getActivity(), R.string.activity_not_available, Toast.LENGTH_SHORT).show();
+      }
+    }
+
+    @Override
+    public void placeVoiceCall(String phoneNumber, String postDialDigits) {
+      Logger.get(getActivity()).logImpression(DialerImpression.Type.CALL_DETAILS_VOICE_CALL_BACK);
+
+      boolean canSupportedAssistedDialing =
+          getActivity()
+              .getIntent()
+              .getExtras()
+              .getBoolean(EXTRA_CAN_SUPPORT_ASSISTED_DIALING, false);
+      CallIntentBuilder callIntentBuilder =
+          new CallIntentBuilder(phoneNumber + postDialDigits, CallInitiationType.Type.CALL_DETAILS);
+      if (canSupportedAssistedDialing) {
+        callIntentBuilder.setAllowAssistedDial(true);
+      }
+
+      PreCall.start(getActivity(), callIntentBuilder);
+    }
+
+    private CallDetailsActivityCommon getActivity() {
+      return Preconditions.checkNotNull(activityWeakReference.get());
+    }
+
+    @Override
+    public void openAssistedDialingSettings(View unused) {
+      Intent intent = new Intent(getActivity(), AssistedDialingSettingActivity.class);
+      getActivity().startActivity(intent);
+    }
+
+    @Override
+    public void createAssistedDialerNumberParserTask(
+        AssistedDialingNumberParseWorker worker,
+        SuccessListener<Integer> successListener,
+        FailureListener failureListener) {
+      DialerExecutorComponent.get(getActivity().getApplicationContext())
+          .dialerExecutorFactory()
+          .createUiTaskBuilder(
+              getActivity().getFragmentManager(),
+              "CallDetailsActivityCommon.createAssistedDialerNumberParserTask",
+              new AssistedDialingNumberParseWorker())
+          .onSuccess(successListener)
+          .onFailure(failureListener)
+          .build()
+          .executeParallel(getActivity().getNumber());
+    }
+  }
+
+  static final class AssistedDialingNumberParseWorker implements Worker<String, Integer> {
+
+    @Override
+    public Integer doInBackground(@NonNull String phoneNumber) {
+      PhoneNumber parsedNumber;
+      try {
+        parsedNumber = PhoneNumberUtil.getInstance().parse(phoneNumber, null);
+      } catch (NumberParseException e) {
+        LogUtil.w(
+            "AssistedDialingNumberParseWorker.doInBackground",
+            "couldn't parse phone number: " + LogUtil.sanitizePii(phoneNumber),
+            e);
+        return 0;
+      }
+      return parsedNumber.getCountryCode();
+    }
+  }
+
+  private static final class DeleteCallDetailsListener
+      implements CallDetailsFooterViewHolder.DeleteCallDetailsListener {
+
+    private final WeakReference<CallDetailsActivityCommon> activityWeakReference;
+
+    DeleteCallDetailsListener(CallDetailsActivityCommon activity) {
+      this.activityWeakReference = new WeakReference<>(activity);
+    }
+
+    @Override
+    public void delete() {
+      CallDetailsActivityCommon activity = getActivity();
+      DialerExecutorComponent.get(activity)
+          .dialerExecutorFactory()
+          .createNonUiTaskBuilder(new DeleteCallsWorker(activity))
+          .onSuccess(
+              unused -> {
+                Intent data = new Intent();
+                data.putExtra(EXTRA_PHONE_NUMBER, activity.getNumber());
+                for (CallDetailsEntry entry : activity.getCallDetailsEntries().getEntriesList()) {
+                  if (entry.getHistoryResultsCount() > 0) {
+                    data.putExtra(EXTRA_HAS_ENRICHED_CALL_DATA, true);
+                    break;
+                  }
+                }
+
+                activity.setResult(RESULT_OK, data);
+                activity.finish();
+              })
+          .build()
+          .executeSerial(activity.getCallDetailsEntries());
+    }
+
+    private CallDetailsActivityCommon getActivity() {
+      return Preconditions.checkNotNull(activityWeakReference.get());
+    }
+  }
+
+  private static final class ReportCallIdListener
+      implements CallDetailsFooterViewHolder.ReportCallIdListener {
+    private final WeakReference<Activity> activityWeakReference;
+
+    ReportCallIdListener(Activity activity) {
+      this.activityWeakReference = new WeakReference<>(activity);
+    }
+
+    @Override
+    public void reportCallId(String number) {
+      ReportDialogFragment.newInstance(number)
+          .show(getActivity().getFragmentManager(), null /* tag */);
+    }
+
+    @Override
+    public boolean canReportCallerId(String number) {
+      return getActivity().getIntent().getExtras().getBoolean(EXTRA_CAN_REPORT_CALLER_ID, false);
+    }
+
+    private Activity getActivity() {
+      return Preconditions.checkNotNull(activityWeakReference.get());
+    }
+  }
+
+  private static final class EnrichedCallHistoricalDataChangedListener
+      implements EnrichedCallManager.HistoricalDataChangedListener {
+    private final WeakReference<CallDetailsActivityCommon> activityWeakReference;
+
+    EnrichedCallHistoricalDataChangedListener(CallDetailsActivityCommon activity) {
+      this.activityWeakReference = new WeakReference<>(activity);
+    }
+
+    @Override
+    public void onHistoricalDataChanged() {
+      CallDetailsActivityCommon activity = getActivity();
+      Map<CallDetailsEntry, List<HistoryResult>> mappedResults =
+          getAllHistoricalData(activity.getNumber(), activity.callDetailsEntries);
+
+      activity.adapter.updateCallDetailsEntries(
+          generateAndMapNewCallDetailsEntriesHistoryResults(
+              activity.getNumber(), activity.callDetailsEntries, mappedResults));
+    }
+
+    private CallDetailsActivityCommon getActivity() {
+      return Preconditions.checkNotNull(activityWeakReference.get());
+    }
+
+    @NonNull
+    private Map<CallDetailsEntry, List<HistoryResult>> getAllHistoricalData(
+        @Nullable String number, @NonNull CallDetailsEntries entries) {
+      if (number == null) {
+        return Collections.emptyMap();
+      }
+
+      Map<CallDetailsEntry, List<HistoryResult>> historicalData =
+          EnrichedCallComponent.get(getActivity())
+              .getEnrichedCallManager()
+              .getAllHistoricalData(number, entries);
+      if (historicalData == null) {
+        return Collections.emptyMap();
+      }
+      return historicalData;
+    }
+
+    private static CallDetailsEntries generateAndMapNewCallDetailsEntriesHistoryResults(
+        @Nullable String number,
+        @NonNull CallDetailsEntries callDetailsEntries,
+        @NonNull Map<CallDetailsEntry, List<HistoryResult>> mappedResults) {
+      if (number == null) {
+        return callDetailsEntries;
+      }
+      CallDetailsEntries.Builder mutableCallDetailsEntries = CallDetailsEntries.newBuilder();
+      for (CallDetailsEntry entry : callDetailsEntries.getEntriesList()) {
+        CallDetailsEntry.Builder newEntry = CallDetailsEntry.newBuilder().mergeFrom(entry);
+        List<HistoryResult> results = mappedResults.get(entry);
+        if (results != null) {
+          newEntry.addAllHistoryResults(mappedResults.get(entry));
+          LogUtil.v(
+              "CallDetailsActivityCommon.generateAndMapNewCallDetailsEntriesHistoryResults",
+              "mapped %d results",
+              newEntry.getHistoryResultsList().size());
+        }
+        mutableCallDetailsEntries.addEntries(newEntry.build());
+      }
+      return mutableCallDetailsEntries.build();
+    }
+  }
+}
diff --git a/java/com/android/dialer/calldetails/CallDetailsAdapter.java b/java/com/android/dialer/calldetails/CallDetailsAdapter.java
index 030366e..dfa472a 100644
--- a/java/com/android/dialer/calldetails/CallDetailsAdapter.java
+++ b/java/com/android/dialer/calldetails/CallDetailsAdapter.java
@@ -17,119 +17,55 @@
 package com.android.dialer.calldetails;
 
 import android.content.Context;
-import android.support.annotation.NonNull;
 import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-import com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry;
+import android.view.View;
 import com.android.dialer.calldetails.CallDetailsFooterViewHolder.DeleteCallDetailsListener;
 import com.android.dialer.calldetails.CallDetailsHeaderViewHolder.CallDetailsHeaderListener;
-import com.android.dialer.calllogutils.CallTypeHelper;
-import com.android.dialer.calllogutils.CallbackActionHelper;
-import com.android.dialer.calllogutils.CallbackActionHelper.CallbackAction;
-import com.android.dialer.common.Assert;
 import com.android.dialer.dialercontact.DialerContact;
-import com.android.dialer.duo.DuoComponent;
-import java.util.List;
 
-/** Adapter for RecyclerView in {@link CallDetailsActivity}. */
-final class CallDetailsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
-
-  private static final int HEADER_VIEW_TYPE = 1;
-  private static final int CALL_ENTRY_VIEW_TYPE = 2;
-  private static final int FOOTER_VIEW_TYPE = 3;
+/**
+ * A {@link RecyclerView.Adapter} for {@link CallDetailsActivity}.
+ *
+ * <p>See {@link CallDetailsAdapterCommon} for logic shared between this adapter and {@link
+ * OldCallDetailsAdapter}.
+ */
+final class CallDetailsAdapter extends CallDetailsAdapterCommon {
 
   private final DialerContact contact;
-  private final CallDetailsHeaderListener callDetailsHeaderListener;
-  private final CallDetailsFooterViewHolder.ReportCallIdListener reportCallIdListener;
-  private final DeleteCallDetailsListener deleteCallDetailsListener;
-  private final CallTypeHelper callTypeHelper;
-  private List<CallDetailsEntry> callDetailsEntries;
 
   CallDetailsAdapter(
       Context context,
-      @NonNull DialerContact contact,
-      @NonNull List<CallDetailsEntry> callDetailsEntries,
+      DialerContact contact,
+      CallDetailsEntries callDetailsEntries,
       CallDetailsHeaderListener callDetailsHeaderListener,
       CallDetailsFooterViewHolder.ReportCallIdListener reportCallIdListener,
       DeleteCallDetailsListener deleteCallDetailsListener) {
-    this.contact = Assert.isNotNull(contact);
-    this.callDetailsEntries = callDetailsEntries;
-    this.callDetailsHeaderListener = callDetailsHeaderListener;
-    this.reportCallIdListener = reportCallIdListener;
-    this.deleteCallDetailsListener = deleteCallDetailsListener;
-    callTypeHelper = new CallTypeHelper(context.getResources(), DuoComponent.get(context).getDuo());
+    super(
+        context,
+        callDetailsEntries,
+        callDetailsHeaderListener,
+        reportCallIdListener,
+        deleteCallDetailsListener);
+    this.contact = contact;
   }
 
   @Override
-  public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-    switch (viewType) {
-      case HEADER_VIEW_TYPE:
-        return new CallDetailsHeaderViewHolder(
-            inflater.inflate(R.layout.contact_container, parent, false), callDetailsHeaderListener);
-      case CALL_ENTRY_VIEW_TYPE:
-        return new CallDetailsEntryViewHolder(
-            inflater.inflate(R.layout.call_details_entry, parent, false));
-      case FOOTER_VIEW_TYPE:
-        return new CallDetailsFooterViewHolder(
-            inflater.inflate(R.layout.call_details_footer, parent, false),
-            reportCallIdListener,
-            deleteCallDetailsListener);
-      default:
-        throw Assert.createIllegalStateFailException(
-            "No ViewHolder available for viewType: " + viewType);
-    }
+  protected CallDetailsHeaderViewHolder createCallDetailsHeaderViewHolder(
+      View container, CallDetailsHeaderListener callDetailsHeaderListener) {
+    return new CallDetailsHeaderViewHolder(
+        container, contact.getNumber(), contact.getPostDialDigits(), callDetailsHeaderListener);
   }
 
   @Override
-  public void onBindViewHolder(ViewHolder holder, int position) {
-    if (position == 0) { // Header
-      ((CallDetailsHeaderViewHolder) holder).updateContactInfo(contact, getCallbackAction());
-      ((CallDetailsHeaderViewHolder) holder)
-          .updateAssistedDialingInfo(callDetailsEntries.get(position));
-    } else if (position == getItemCount() - 1) {
-      ((CallDetailsFooterViewHolder) holder).setPhoneNumber(contact.getNumber());
-    } else {
-      CallDetailsEntryViewHolder viewHolder = (CallDetailsEntryViewHolder) holder;
-      CallDetailsEntry entry = callDetailsEntries.get(position - 1);
-      viewHolder.setCallDetails(
-          contact.getNumber(),
-          entry,
-          callTypeHelper,
-          !entry.getHistoryResultsList().isEmpty() && position != getItemCount() - 2);
-    }
+  protected void bindCallDetailsHeaderViewHolder(
+      CallDetailsHeaderViewHolder callDetailsHeaderViewHolder, int position) {
+    callDetailsHeaderViewHolder.updateContactInfo(contact, getCallbackAction());
+    callDetailsHeaderViewHolder.updateAssistedDialingInfo(
+        getCallDetailsEntries().getEntries(position));
   }
 
   @Override
-  public int getItemViewType(int position) {
-    if (position == 0) { // Header
-      return HEADER_VIEW_TYPE;
-    } else if (position == getItemCount() - 1) {
-      return FOOTER_VIEW_TYPE;
-    } else {
-      return CALL_ENTRY_VIEW_TYPE;
-    }
-  }
-
-  @Override
-  public int getItemCount() {
-    return callDetailsEntries.isEmpty()
-        ? 0
-        : callDetailsEntries.size() + 2; // plus header and footer
-  }
-
-  void updateCallDetailsEntries(List<CallDetailsEntry> entries) {
-    callDetailsEntries = entries;
-    notifyDataSetChanged();
-  }
-
-  private @CallbackAction int getCallbackAction() {
-    Assert.checkState(!callDetailsEntries.isEmpty());
-
-    CallDetailsEntry entry = callDetailsEntries.get(0);
-    return CallbackActionHelper.getCallbackAction(
-        contact.getNumber(), entry.getFeatures(), entry.getIsDuoCall());
+  protected String getNumber() {
+    return contact.getNumber();
   }
 }
diff --git a/java/com/android/dialer/calldetails/CallDetailsAdapterCommon.java b/java/com/android/dialer/calldetails/CallDetailsAdapterCommon.java
new file mode 100644
index 0000000..27feff8
--- /dev/null
+++ b/java/com/android/dialer/calldetails/CallDetailsAdapterCommon.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2017 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.dialer.calldetails;
+
+import android.content.Context;
+import android.support.annotation.CallSuper;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry;
+import com.android.dialer.calldetails.CallDetailsFooterViewHolder.DeleteCallDetailsListener;
+import com.android.dialer.calldetails.CallDetailsFooterViewHolder.ReportCallIdListener;
+import com.android.dialer.calldetails.CallDetailsHeaderViewHolder.CallDetailsHeaderListener;
+import com.android.dialer.calllogutils.CallTypeHelper;
+import com.android.dialer.calllogutils.CallbackActionHelper;
+import com.android.dialer.calllogutils.CallbackActionHelper.CallbackAction;
+import com.android.dialer.common.Assert;
+import com.android.dialer.duo.DuoComponent;
+
+/**
+ * Contains common logic shared between {@link OldCallDetailsAdapter} and {@link
+ * CallDetailsAdapter}.
+ */
+abstract class CallDetailsAdapterCommon extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+
+  private static final int HEADER_VIEW_TYPE = 1;
+  private static final int CALL_ENTRY_VIEW_TYPE = 2;
+  private static final int FOOTER_VIEW_TYPE = 3;
+
+  private final CallDetailsHeaderListener callDetailsHeaderListener;
+  private final ReportCallIdListener reportCallIdListener;
+  private final DeleteCallDetailsListener deleteCallDetailsListener;
+  private final CallTypeHelper callTypeHelper;
+
+  private CallDetailsEntries callDetailsEntries;
+
+  protected abstract void bindCallDetailsHeaderViewHolder(
+      CallDetailsHeaderViewHolder viewHolder, int position);
+
+  protected abstract CallDetailsHeaderViewHolder createCallDetailsHeaderViewHolder(
+      View container, CallDetailsHeaderListener callDetailsHeaderListener);
+
+  /** Returns the phone number of the call details. */
+  protected abstract String getNumber();
+
+  CallDetailsAdapterCommon(
+      Context context,
+      CallDetailsEntries callDetailsEntries,
+      CallDetailsHeaderListener callDetailsHeaderListener,
+      ReportCallIdListener reportCallIdListener,
+      DeleteCallDetailsListener deleteCallDetailsListener) {
+    this.callDetailsEntries = callDetailsEntries;
+    this.callDetailsHeaderListener = callDetailsHeaderListener;
+    this.reportCallIdListener = reportCallIdListener;
+    this.deleteCallDetailsListener = deleteCallDetailsListener;
+    this.callTypeHelper =
+        new CallTypeHelper(context.getResources(), DuoComponent.get(context).getDuo());
+  }
+
+  @Override
+  @CallSuper
+  public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+    switch (viewType) {
+      case HEADER_VIEW_TYPE:
+        return createCallDetailsHeaderViewHolder(
+            inflater.inflate(R.layout.contact_container, parent, false), callDetailsHeaderListener);
+      case CALL_ENTRY_VIEW_TYPE:
+        return new CallDetailsEntryViewHolder(
+            inflater.inflate(R.layout.call_details_entry, parent, false));
+      case FOOTER_VIEW_TYPE:
+        return new CallDetailsFooterViewHolder(
+            inflater.inflate(R.layout.call_details_footer, parent, false),
+            reportCallIdListener,
+            deleteCallDetailsListener);
+      default:
+        throw Assert.createIllegalStateFailException(
+            "No ViewHolder available for viewType: " + viewType);
+    }
+  }
+
+  @Override
+  @CallSuper
+  public void onBindViewHolder(ViewHolder holder, int position) {
+    if (position == 0) { // Header
+      bindCallDetailsHeaderViewHolder((CallDetailsHeaderViewHolder) holder, position);
+    } else if (position == getItemCount() - 1) {
+      ((CallDetailsFooterViewHolder) holder).setPhoneNumber(getNumber());
+    } else {
+      CallDetailsEntryViewHolder viewHolder = (CallDetailsEntryViewHolder) holder;
+      CallDetailsEntry entry = callDetailsEntries.getEntries(position - 1);
+      viewHolder.setCallDetails(
+          getNumber(),
+          entry,
+          callTypeHelper,
+          !entry.getHistoryResultsList().isEmpty() && position != getItemCount() - 2);
+    }
+  }
+
+  @Override
+  @CallSuper
+  public int getItemViewType(int position) {
+    if (position == 0) { // Header
+      return HEADER_VIEW_TYPE;
+    } else if (position == getItemCount() - 1) {
+      return FOOTER_VIEW_TYPE;
+    } else {
+      return CALL_ENTRY_VIEW_TYPE;
+    }
+  }
+
+  @Override
+  @CallSuper
+  public int getItemCount() {
+    return callDetailsEntries.getEntriesCount() == 0
+        ? 0
+        : callDetailsEntries.getEntriesCount() + 2; // plus header and footer
+  }
+
+  final CallDetailsEntries getCallDetailsEntries() {
+    return callDetailsEntries;
+  }
+
+  final void updateCallDetailsEntries(CallDetailsEntries entries) {
+    callDetailsEntries = entries;
+    notifyDataSetChanged();
+  }
+
+  final @CallbackAction int getCallbackAction() {
+    Assert.checkState(!callDetailsEntries.getEntriesList().isEmpty());
+
+    CallDetailsEntry entry = callDetailsEntries.getEntries(0);
+    return CallbackActionHelper.getCallbackAction(
+        getNumber(), entry.getFeatures(), entry.getIsDuoCall());
+  }
+}
diff --git a/java/com/android/dialer/calldetails/CallDetailsCursorLoader.java b/java/com/android/dialer/calldetails/CallDetailsCursorLoader.java
index 15c64c9..3b8af40 100644
--- a/java/com/android/dialer/calldetails/CallDetailsCursorLoader.java
+++ b/java/com/android/dialer/calldetails/CallDetailsCursorLoader.java
@@ -73,8 +73,8 @@
     // the data loading but no data can be fetched and we want to ensure the data set is not empty
     // when building CallDetailsEntries proto (see toCallDetailsEntries(Cursor)).
     //
-    // CallDetailsActivity doesn't respond to underlying data changes when launched from the old
-    // call log and we decided to keep it that way when launched from the new call log.
+    // OldCallDetailsActivity doesn't respond to underlying data changes and we decided to keep it
+    // that way in CallDetailsActivity.
   }
 
   /**
diff --git a/java/com/android/dialer/calldetails/CallDetailsEntryViewHolder.java b/java/com/android/dialer/calldetails/CallDetailsEntryViewHolder.java
index c65bb91..f50876d 100644
--- a/java/com/android/dialer/calldetails/CallDetailsEntryViewHolder.java
+++ b/java/com/android/dialer/calldetails/CallDetailsEntryViewHolder.java
@@ -40,7 +40,7 @@
 import com.android.dialer.util.DialerUtils;
 import com.android.dialer.util.IntentUtil;
 
-/** ViewHolder for call entries in {@link CallDetailsActivity}. */
+/** ViewHolder for call entries in {@link OldCallDetailsActivity} or {@link CallDetailsActivity}. */
 public class CallDetailsEntryViewHolder extends ViewHolder {
 
   private final CallTypeIconsView callTypeIcon;
diff --git a/java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.java b/java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.java
index 30b28d8..d6e6dbe 100644
--- a/java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.java
+++ b/java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.java
@@ -31,7 +31,7 @@
 import com.android.dialer.util.CallUtil;
 import com.android.dialer.util.DialerUtils;
 
-/** ViewHolder container for {@link CallDetailsActivity} footer. */
+/** ViewHolder for the footer in {@link OldCallDetailsActivity} or {@link CallDetailsActivity}. */
 final class CallDetailsFooterViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
 
   private final ReportCallIdListener reportCallIdListener;
@@ -91,7 +91,8 @@
     } else if (view == delete) {
       deleteCallDetailsListener.delete();
     } else {
-      Assert.fail("View on click not implemented: " + view);
+      throw Assert.createUnsupportedOperationFailException(
+          "View on click not implemented: " + view);
     }
   }
 
diff --git a/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java b/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java
index 647c6ed..34ed688 100644
--- a/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java
+++ b/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java
@@ -27,7 +27,7 @@
 import android.widget.QuickContactBadge;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
-import com.android.dialer.calldetails.CallDetailsActivity.AssistedDialingNumberParseWorker;
+import com.android.dialer.calldetails.CallDetailsActivityCommon.AssistedDialingNumberParseWorker;
 import com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry;
 import com.android.dialer.calllogutils.CallbackActionHelper.CallbackAction;
 import com.android.dialer.common.Assert;
@@ -40,7 +40,11 @@
 import com.android.dialer.logging.InteractionEvent;
 import com.android.dialer.logging.Logger;
 
-/** ViewHolder for Header/Contact in {@link CallDetailsActivity}. */
+/**
+ * ViewHolder for the header in {@link OldCallDetailsActivity} or {@link CallDetailsActivity}.
+ *
+ * <p>The header contains contact info and the primary callback button.
+ */
 public class CallDetailsHeaderViewHolder extends RecyclerView.ViewHolder
     implements OnClickListener, FailureListener {
 
@@ -54,10 +58,16 @@
   private final TextView assistedDialingInternationalDirectDialCodeAndCountryCodeText;
   private final RelativeLayout assistedDialingContainer;
 
-  private DialerContact contact;
+  private final String number;
+  private final String postDialDigits;
+
   private @CallbackAction int callbackAction;
 
-  CallDetailsHeaderViewHolder(View container, CallDetailsHeaderListener callDetailsHeaderListener) {
+  CallDetailsHeaderViewHolder(
+      View container,
+      String number,
+      String postDialDigits,
+      CallDetailsHeaderListener callDetailsHeaderListener) {
     super(container);
     context = container.getContext();
     callbackButton = container.findViewById(R.id.call_back_button);
@@ -73,7 +83,11 @@
         callDetailsHeaderListener::openAssistedDialingSettings);
 
     callbackButton.setOnClickListener(this);
+
+    this.number = number;
+    this.postDialDigits = postDialDigits;
     this.callDetailsHeaderListener = callDetailsHeaderListener;
+
     Logger.get(context)
         .logQuickContactOnTouch(
             contactPhoto, InteractionEvent.Type.OPEN_QUICK_CONTACT_FROM_CALL_DETAILS, true);
@@ -89,7 +103,7 @@
     if (callDetailsEntry != null && hasAssistedDialingFeature(callDetailsEntry.getFeatures())) {
       showAssistedDialingContainer(true);
       callDetailsHeaderListener.createAssistedDialerNumberParserTask(
-          new CallDetailsActivity.AssistedDialingNumberParseWorker(),
+          new CallDetailsActivityCommon.AssistedDialingNumberParseWorker(),
           this::updateAssistedDialingText,
           this::onFailure);
 
@@ -132,7 +146,6 @@
 
   /** Populates the contact info fields based on the current contact information. */
   void updateContactInfo(DialerContact contact, @CallbackAction int callbackAction) {
-    this.contact = contact;
     ContactPhotoManager.getInstance(context)
         .loadDialerThumbnailOrPhoto(
             contactPhoto,
@@ -194,14 +207,13 @@
     if (view == callbackButton) {
       switch (callbackAction) {
         case CallbackAction.IMS_VIDEO:
-          callDetailsHeaderListener.placeImsVideoCall(contact.getNumber());
+          callDetailsHeaderListener.placeImsVideoCall(number);
           break;
         case CallbackAction.DUO:
-          callDetailsHeaderListener.placeDuoVideoCall(contact.getNumber());
+          callDetailsHeaderListener.placeDuoVideoCall(number);
           break;
         case CallbackAction.VOICE:
-          callDetailsHeaderListener.placeVoiceCall(
-              contact.getNumber(), contact.getPostDialDigits());
+          callDetailsHeaderListener.placeVoiceCall(number, postDialDigits);
           break;
         case CallbackAction.NONE:
         default:
diff --git a/java/com/android/dialer/calldetails/OldCallDetailsActivity.java b/java/com/android/dialer/calldetails/OldCallDetailsActivity.java
new file mode 100644
index 0000000..1891265
--- /dev/null
+++ b/java/com/android/dialer/calldetails/OldCallDetailsActivity.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 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.dialer.calldetails;
+
+import android.content.Context;
+import android.content.Intent;
+import com.android.dialer.calldetails.CallDetailsFooterViewHolder.DeleteCallDetailsListener;
+import com.android.dialer.calldetails.CallDetailsFooterViewHolder.ReportCallIdListener;
+import com.android.dialer.calldetails.CallDetailsHeaderViewHolder.CallDetailsHeaderListener;
+import com.android.dialer.common.Assert;
+import com.android.dialer.dialercontact.DialerContact;
+import com.android.dialer.protos.ProtoParsers;
+
+/**
+ * Displays the details of a specific call log entry.
+ *
+ * <p>This activity is for the old call log.
+ *
+ * <p>See {@link CallDetailsAdapterCommon} for logic shared between this activity and the one for
+ * the new call log.
+ */
+public final class OldCallDetailsActivity extends CallDetailsActivityCommon {
+  public static final String EXTRA_CALL_DETAILS_ENTRIES = "call_details_entries";
+  public static final String EXTRA_CONTACT = "contact";
+
+  /** Contains info to be shown in the header. */
+  private DialerContact contact;
+
+  public static boolean isLaunchIntent(Intent intent) {
+    return intent.getComponent() != null
+        && OldCallDetailsActivity.class.getName().equals(intent.getComponent().getClassName());
+  }
+
+  /** Returns an {@link Intent} to launch this activity. */
+  public static Intent newInstance(
+      Context context,
+      CallDetailsEntries details,
+      DialerContact contact,
+      boolean canReportCallerId,
+      boolean canSupportAssistedDialing) {
+    Intent intent = new Intent(context, OldCallDetailsActivity.class);
+    ProtoParsers.put(intent, EXTRA_CONTACT, Assert.isNotNull(contact));
+    ProtoParsers.put(intent, EXTRA_CALL_DETAILS_ENTRIES, Assert.isNotNull(details));
+    intent.putExtra(EXTRA_CAN_REPORT_CALLER_ID, canReportCallerId);
+    intent.putExtra(EXTRA_CAN_SUPPORT_ASSISTED_DIALING, canSupportAssistedDialing);
+    return intent;
+  }
+
+  @Override
+  protected void handleIntent(Intent intent) {
+    Assert.checkArgument(intent.hasExtra(EXTRA_CONTACT));
+    Assert.checkArgument(intent.hasExtra(EXTRA_CALL_DETAILS_ENTRIES));
+    Assert.checkArgument(intent.hasExtra(EXTRA_CAN_REPORT_CALLER_ID));
+    Assert.checkArgument(intent.hasExtra(EXTRA_CAN_SUPPORT_ASSISTED_DIALING));
+
+    contact = ProtoParsers.getTrusted(intent, EXTRA_CONTACT, DialerContact.getDefaultInstance());
+    setCallDetailsEntries(
+        ProtoParsers.getTrusted(
+            intent, EXTRA_CALL_DETAILS_ENTRIES, CallDetailsEntries.getDefaultInstance()));
+  }
+
+  @Override
+  protected CallDetailsAdapterCommon createAdapter(
+      CallDetailsHeaderListener callDetailsHeaderListener,
+      ReportCallIdListener reportCallIdListener,
+      DeleteCallDetailsListener deleteCallDetailsListener) {
+    return new OldCallDetailsAdapter(
+        /* context = */ this,
+        contact,
+        getCallDetailsEntries(),
+        callDetailsHeaderListener,
+        reportCallIdListener,
+        deleteCallDetailsListener);
+  }
+
+  @Override
+  protected String getNumber() {
+    return contact.getNumber();
+  }
+}
diff --git a/java/com/android/dialer/calldetails/OldCallDetailsAdapter.java b/java/com/android/dialer/calldetails/OldCallDetailsAdapter.java
new file mode 100644
index 0000000..010f5cd
--- /dev/null
+++ b/java/com/android/dialer/calldetails/OldCallDetailsAdapter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 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.dialer.calldetails;
+
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import com.android.dialer.calldetails.CallDetailsFooterViewHolder.DeleteCallDetailsListener;
+import com.android.dialer.calldetails.CallDetailsHeaderViewHolder.CallDetailsHeaderListener;
+import com.android.dialer.dialercontact.DialerContact;
+
+/**
+ * A {@link RecyclerView.Adapter} for {@link OldCallDetailsActivity}.
+ *
+ * <p>See {@link CallDetailsAdapterCommon} for logic shared between this adapter and {@link
+ * CallDetailsAdapter}.
+ */
+final class OldCallDetailsAdapter extends CallDetailsAdapterCommon {
+
+  /** Contains info to be shown in the header. */
+  private final DialerContact contact;
+
+  OldCallDetailsAdapter(
+      Context context,
+      DialerContact contact,
+      CallDetailsEntries callDetailsEntries,
+      CallDetailsHeaderListener callDetailsHeaderListener,
+      CallDetailsFooterViewHolder.ReportCallIdListener reportCallIdListener,
+      DeleteCallDetailsListener deleteCallDetailsListener) {
+    super(
+        context,
+        callDetailsEntries,
+        callDetailsHeaderListener,
+        reportCallIdListener,
+        deleteCallDetailsListener);
+    this.contact = contact;
+  }
+
+  @Override
+  protected CallDetailsHeaderViewHolder createCallDetailsHeaderViewHolder(
+      View container, CallDetailsHeaderListener callDetailsHeaderListener) {
+    return new CallDetailsHeaderViewHolder(
+        container, contact.getNumber(), contact.getPostDialDigits(), callDetailsHeaderListener);
+  }
+
+  @Override
+  protected void bindCallDetailsHeaderViewHolder(
+      CallDetailsHeaderViewHolder callDetailsHeaderViewHolder, int position) {
+    callDetailsHeaderViewHolder.updateContactInfo(contact, getCallbackAction());
+    callDetailsHeaderViewHolder.updateAssistedDialingInfo(
+        getCallDetailsEntries().getEntries(position));
+  }
+
+  @Override
+  protected String getNumber() {
+    return contact.getNumber();
+  }
+}
diff --git a/java/com/android/dialer/calldetails/ReportDialogFragment.java b/java/com/android/dialer/calldetails/ReportDialogFragment.java
index 0861c9d..d75fe51 100644
--- a/java/com/android/dialer/calldetails/ReportDialogFragment.java
+++ b/java/com/android/dialer/calldetails/ReportDialogFragment.java
@@ -38,7 +38,7 @@
 import com.android.dialer.phonenumbercache.CachedNumberLookupService.CachedContactInfo;
 import com.android.dialer.phonenumbercache.PhoneNumberCache;
 
-/** Dialog for reporting an inaccurate caller id information in {@link CallDetailsActivity}. */
+/** Dialog for reporting an inaccurate caller id information. */
 public class ReportDialogFragment extends DialogFragment {
 
   private static final String KEY_NUMBER = "number";
diff --git a/java/com/android/dialer/calllog/CallLogConfig.java b/java/com/android/dialer/calllog/CallLogConfig.java
new file mode 100644
index 0000000..84400e4
--- /dev/null
+++ b/java/com/android/dialer/calllog/CallLogConfig.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 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.dialer.calllog;
+
+import android.content.SharedPreferences;
+import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
+import com.android.dialer.configprovider.ConfigProvider;
+import com.android.dialer.storage.Unencrypted;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import javax.inject.Inject;
+
+/**
+ * Determines if new call log components are enabled.
+ *
+ * <p>When the underlying flag values from the {@link ConfigProvider} changes, it is necessary to do
+ * work such as registering/unregistering content observers, and this class is responsible for
+ * coordinating that work.
+ *
+ * <p>New UI application components should use this class instead of reading flags directly from the
+ * {@link ConfigProvider}.
+ */
+public final class CallLogConfig {
+
+  private static final String NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY = "newCallLogFragmentEnabled";
+  private static final String NEW_VOICEMAIL_FRAGMENT_ENABLED_PREF_KEY =
+      "newVoicemailFragmentEnabled";
+  private static final String NEW_PEER_ENABLED_PREF_KEY = "newPeerEnabled";
+  private static final String NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY =
+      "newCallLogFrameworkEnabled";
+
+  private final SharedPreferences sharedPreferences;
+  private final ConfigProvider configProvider;
+  private final ListeningExecutorService backgroundExecutor;
+
+  @Inject
+  CallLogConfig(
+      @Unencrypted SharedPreferences sharedPreferences,
+      ConfigProvider configProvider,
+      @BackgroundExecutor ListeningExecutorService backgroundExecutor) {
+    this.sharedPreferences = sharedPreferences;
+    this.configProvider = configProvider;
+    this.backgroundExecutor = backgroundExecutor;
+  }
+
+  /**
+   * Updates the config values. This may kick off a lot of work so should be done infrequently, for
+   * example by a scheduled job or broadcast receiver which rarely fires.
+   */
+  public ListenableFuture<Void> update() {
+    return backgroundExecutor.submit(
+        () -> {
+          boolean newCallLogFragmentEnabledInConfigProvider =
+              configProvider.getBoolean("new_call_log_fragment_enabled", false);
+          boolean newVoicemailFragmentEnabledInConfigProvider =
+              configProvider.getBoolean("new_voicemail_fragment_enabled", false);
+          boolean newPeerEnabledInConfigProvider =
+              configProvider.getBoolean("nui_peer_enabled", false);
+
+          boolean isCallLogFrameworkEnabled = isCallLogFrameworkEnabled();
+          boolean callLogFrameworkShouldBeEnabled =
+              newCallLogFragmentEnabledInConfigProvider
+                  || newVoicemailFragmentEnabledInConfigProvider
+                  || newPeerEnabledInConfigProvider;
+
+          if (callLogFrameworkShouldBeEnabled && !isCallLogFrameworkEnabled) {
+            enableFramework();
+
+            // Reflect the flag changes only after the framework is enabled.
+            sharedPreferences
+                .edit()
+                .putBoolean(
+                    NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY,
+                    newCallLogFragmentEnabledInConfigProvider)
+                .putBoolean(
+                    NEW_VOICEMAIL_FRAGMENT_ENABLED_PREF_KEY,
+                    newVoicemailFragmentEnabledInConfigProvider)
+                .putBoolean(NEW_PEER_ENABLED_PREF_KEY, newPeerEnabledInConfigProvider)
+                .putBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, true)
+                .apply();
+
+          } else if (!callLogFrameworkShouldBeEnabled && isCallLogFrameworkEnabled) {
+            // Reflect the flag changes before disabling the framework.
+            sharedPreferences
+                .edit()
+                .putBoolean(NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY, false)
+                .putBoolean(NEW_VOICEMAIL_FRAGMENT_ENABLED_PREF_KEY, false)
+                .putBoolean(NEW_PEER_ENABLED_PREF_KEY, false)
+                .putBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, false)
+                .apply();
+
+            disableFramework();
+          } else {
+            // We didn't need to enable/disable the framework, but we still need to update the
+            // individual flags.
+            sharedPreferences
+                .edit()
+                .putBoolean(
+                    NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY,
+                    newCallLogFragmentEnabledInConfigProvider)
+                .putBoolean(
+                    NEW_VOICEMAIL_FRAGMENT_ENABLED_PREF_KEY,
+                    newVoicemailFragmentEnabledInConfigProvider)
+                .putBoolean(NEW_PEER_ENABLED_PREF_KEY, newPeerEnabledInConfigProvider)
+                .apply();
+          }
+          return null;
+        });
+  }
+
+  private void enableFramework() {
+    // TODO(zachh): Register content observers, etc.
+  }
+
+  private void disableFramework() {
+    // TODO(zachh): Unregister content observers, delete databases, etc.
+  }
+
+  public boolean isNewCallLogFragmentEnabled() {
+    return sharedPreferences.getBoolean(NEW_CALL_LOG_FRAGMENT_ENABLED_PREF_KEY, false);
+  }
+
+  public boolean isNewVoicemailFragmentEnabled() {
+    return sharedPreferences.getBoolean(NEW_VOICEMAIL_FRAGMENT_ENABLED_PREF_KEY, false);
+  }
+
+  public boolean isNewPeerEnabled() {
+    return sharedPreferences.getBoolean(NEW_PEER_ENABLED_PREF_KEY, false);
+  }
+
+  /**
+   * Returns true if the new call log framework is enabled, meaning that content observers are
+   * firing and PhoneLookupHistory is being populated, etc.
+   */
+  public boolean isCallLogFrameworkEnabled() {
+    return sharedPreferences.getBoolean(NEW_CALL_LOG_FRAMEWORK_ENABLED_PREF_KEY, false);
+  }
+}
diff --git a/java/com/android/dialer/constants/ActivityRequestCodes.java b/java/com/android/dialer/constants/ActivityRequestCodes.java
index 4fe3e29..66c38fa 100644
--- a/java/com/android/dialer/constants/ActivityRequestCodes.java
+++ b/java/com/android/dialer/constants/ActivityRequestCodes.java
@@ -35,6 +35,6 @@
   /** Request code for {@link Duo#getIntent(android.content.Context, String)}. */
   public static final int DIALTACTS_DUO = 3;
 
-  /** Request code for {@link com.android.dialer.calldetails.CallDetailsActivity} intent. */
+  /** Request code for {@link com.android.dialer.calldetails.OldCallDetailsActivity} intent. */
   public static final int DIALTACTS_CALL_DETAILS = 4;
 }
diff --git a/java/com/android/dialer/main/impl/OldMainActivityPeer.java b/java/com/android/dialer/main/impl/OldMainActivityPeer.java
index 03afef4..9f15d28 100644
--- a/java/com/android/dialer/main/impl/OldMainActivityPeer.java
+++ b/java/com/android/dialer/main/impl/OldMainActivityPeer.java
@@ -61,7 +61,7 @@
 import com.android.dialer.app.list.PhoneFavoriteSquareTileView;
 import com.android.dialer.app.list.RemoveView;
 import com.android.dialer.callcomposer.CallComposerActivity;
-import com.android.dialer.calldetails.CallDetailsActivity;
+import com.android.dialer.calldetails.OldCallDetailsActivity;
 import com.android.dialer.callintent.CallIntentBuilder;
 import com.android.dialer.callintent.CallSpecificAppData;
 import com.android.dialer.common.FragmentUtils.FragmentUtilListener;
@@ -506,8 +506,8 @@
     } else if (requestCode == ActivityRequestCodes.DIALTACTS_CALL_DETAILS) {
       if (resultCode == AppCompatActivity.RESULT_OK
           && data != null
-          && data.getBooleanExtra(CallDetailsActivity.EXTRA_HAS_ENRICHED_CALL_DATA, false)) {
-        String number = data.getStringExtra(CallDetailsActivity.EXTRA_PHONE_NUMBER);
+          && data.getBooleanExtra(OldCallDetailsActivity.EXTRA_HAS_ENRICHED_CALL_DATA, false)) {
+        String number = data.getStringExtra(OldCallDetailsActivity.EXTRA_PHONE_NUMBER);
         int snackbarDurationMillis = 5_000;
         Snackbar.make(
                 snackbarContainer,