Merge changes I40cf6e7d,Iff515ae2,If8ca1f57,I1eec7a73,Iccb4e72c, ... am: e479c7d417
am: 7d70347147

Change-Id: I197ab6614424109e822301125eb8373dbc067e84
diff --git a/java/com/android/dialer/app/calllog/CallLogAdapter.java b/java/com/android/dialer/app/calllog/CallLogAdapter.java
index f5474ee..485683b 100644
--- a/java/com/android/dialer/app/calllog/CallLogAdapter.java
+++ b/java/com/android/dialer/app/calllog/CallLogAdapter.java
@@ -100,6 +100,8 @@
 import java.util.ArrayList;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 /** Adapter class to fill in data for the Call Log. */
 public class CallLogAdapter extends GroupingListAdapter
@@ -158,9 +160,12 @@
   /**
    * Maps a raw input number to match info. We only log one MatchInfo per raw input number to reduce
    * the amount of data logged.
+   *
+   * <p>Note that this has to be a {@link ConcurrentMap} as the match info for each row in the UI is
+   * loaded in a background thread spawned when the ViewHolder is bound.
    */
-  private final Map<String, ContactsProviderMatchInfo> contactsProviderMatchInfos =
-      new ArrayMap<>();
+  private final ConcurrentMap<String, ContactsProviderMatchInfo> contactsProviderMatchInfos =
+      new ConcurrentHashMap<>();
 
   private final ActionMode.Callback actionModeCallback =
       new ActionMode.Callback() {
@@ -1464,6 +1469,7 @@
     notifyDataSetChanged();
   }
 
+  @WorkerThread
   private void logCp2Metrics(PhoneCallDetails details, ContactInfo contactInfo) {
     if (details == null) {
       return;
diff --git a/java/com/android/dialer/calllog/CallLogCacheUpdater.java b/java/com/android/dialer/calllog/CallLogCacheUpdater.java
index a7b2b3d..b3130e9 100644
--- a/java/com/android/dialer/calllog/CallLogCacheUpdater.java
+++ b/java/com/android/dialer/calllog/CallLogCacheUpdater.java
@@ -25,6 +25,7 @@
 import android.provider.CallLog;
 import android.provider.CallLog.Calls;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.support.annotation.VisibleForTesting;
 import com.android.dialer.DialerPhoneNumber;
 import com.android.dialer.NumberAttributes;
 import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog;
@@ -33,6 +34,7 @@
 import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
 import com.android.dialer.inject.ApplicationContext;
 import com.android.dialer.protos.ProtoParsers;
+import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
 import java.util.ArrayList;
@@ -49,13 +51,23 @@
 
   private final Context appContext;
   private final ListeningExecutorService backgroundExecutor;
+  private final CallLogState callLogState;
+
+  /**
+   * Maximum numbers of operations the updater can do. Each transaction to the system call log will
+   * trigger a call log refresh, so the updater can only do a single batch. If there are more
+   * operations it will be truncated. Under normal circumstances there will only be 1 operation
+   */
+  @VisibleForTesting static final int CACHE_UPDATE_LIMIT = 100;
 
   @Inject
   CallLogCacheUpdater(
       @ApplicationContext Context appContext,
-      @BackgroundExecutor ListeningExecutorService backgroundExecutor) {
+      @BackgroundExecutor ListeningExecutorService backgroundExecutor,
+      CallLogState callLogState) {
     this.appContext = appContext;
     this.backgroundExecutor = backgroundExecutor;
+    this.callLogState = callLogState;
   }
 
   /**
@@ -66,17 +78,27 @@
    * has changed
    */
   public ListenableFuture<Void> updateCache(CallLogMutations mutations) {
-    return backgroundExecutor.submit(
-        () -> {
+    return Futures.transform(
+        callLogState.isBuilt(),
+        isBuilt -> {
+          if (!isBuilt) {
+            // Initial build might need to update 1000 caches, which may overflow the batch
+            // operation limit. The initial data was already built with the cache, there's no need
+            // to update it.
+            LogUtil.i("CallLogCacheUpdater.updateCache", "not updating cache for initial build");
+            return null;
+          }
           updateCacheInternal(mutations);
           return null;
-        });
+        },
+        backgroundExecutor);
   }
 
   private void updateCacheInternal(CallLogMutations mutations) {
     ArrayList<ContentProviderOperation> operations = new ArrayList<>();
     Stream.concat(
             mutations.getInserts().entrySet().stream(), mutations.getUpdates().entrySet().stream())
+        .limit(CACHE_UPDATE_LIMIT)
         .forEach(
             entry -> {
               ContentValues values = entry.getValue();
diff --git a/java/com/android/dialer/calllog/database/Coalescer.java b/java/com/android/dialer/calllog/database/Coalescer.java
index 2ad9f9a..fd751e7 100644
--- a/java/com/android/dialer/calllog/database/Coalescer.java
+++ b/java/com/android/dialer/calllog/database/Coalescer.java
@@ -16,6 +16,7 @@
 package com.android.dialer.calllog.database;
 
 import android.database.Cursor;
+import android.database.StaleDataException;
 import android.provider.CallLog.Calls;
 import android.support.annotation.NonNull;
 import android.support.annotation.WorkerThread;
@@ -85,34 +86,72 @@
   @WorkerThread
   @NonNull
   private ImmutableList<CoalescedRow> coalesceInternal(
-      Cursor allAnnotatedCallLogRowsSortedByTimestampDesc) {
+      Cursor allAnnotatedCallLogRowsSortedByTimestampDesc) throws ExpectedCoalescerException {
     Assert.isWorkerThread();
 
-    if (!allAnnotatedCallLogRowsSortedByTimestampDesc.moveToFirst()) {
-      return ImmutableList.of();
-    }
-
     ImmutableList.Builder<CoalescedRow> coalescedRowListBuilder = new ImmutableList.Builder<>();
 
-    RowCombiner rowCombiner = new RowCombiner(allAnnotatedCallLogRowsSortedByTimestampDesc);
-    rowCombiner.startNewGroup();
-
-    long coalescedRowId = 0;
-    do {
-      boolean isRowMerged = rowCombiner.mergeRow(allAnnotatedCallLogRowsSortedByTimestampDesc);
-
-      if (isRowMerged) {
-        allAnnotatedCallLogRowsSortedByTimestampDesc.moveToNext();
+    try {
+      if (!allAnnotatedCallLogRowsSortedByTimestampDesc.moveToFirst()) {
+        return ImmutableList.of();
       }
 
-      if (!isRowMerged || allAnnotatedCallLogRowsSortedByTimestampDesc.isAfterLast()) {
-        coalescedRowListBuilder.add(
-            rowCombiner.combine().toBuilder().setId(coalescedRowId++).build());
-        rowCombiner.startNewGroup();
-      }
-    } while (!allAnnotatedCallLogRowsSortedByTimestampDesc.isAfterLast());
+      RowCombiner rowCombiner = new RowCombiner(allAnnotatedCallLogRowsSortedByTimestampDesc);
+      rowCombiner.startNewGroup();
 
-    return coalescedRowListBuilder.build();
+      long coalescedRowId = 0;
+      do {
+        boolean isRowMerged = rowCombiner.mergeRow(allAnnotatedCallLogRowsSortedByTimestampDesc);
+
+        if (isRowMerged) {
+          allAnnotatedCallLogRowsSortedByTimestampDesc.moveToNext();
+        }
+
+        if (!isRowMerged || allAnnotatedCallLogRowsSortedByTimestampDesc.isAfterLast()) {
+          coalescedRowListBuilder.add(
+              rowCombiner.combine().toBuilder().setId(coalescedRowId++).build());
+          rowCombiner.startNewGroup();
+        }
+      } while (!allAnnotatedCallLogRowsSortedByTimestampDesc.isAfterLast());
+
+      return coalescedRowListBuilder.build();
+
+    } catch (Exception exception) {
+      // Coalescing can fail if cursor "allAnnotatedCallLogRowsSortedByTimestampDesc" is closed by
+      // its loader while the work is still in progress.
+      //
+      // This can happen when the loader restarts and finishes loading data before the coalescing
+      // work is completed.
+      //
+      // This kind of failure doesn't have to crash the app as coalescing will be restarted on the
+      // latest data obtained by the loader. Therefore, we inspect the exception here and throw an
+      // ExpectedCoalescerException if it is the case described above.
+      //
+      // The type of expected exception depends on whether AbstractWindowedCursor#checkPosition() is
+      // called when the cursor is closed.
+      //   (1) If it is called before the cursor is closed, we will get IllegalStateException thrown
+      //       by SQLiteClosable when it attempts to acquire a reference to the database.
+      //   (2) Otherwise, we will get StaleDataException thrown by AbstractWindowedCursor's
+      //       checkPosition() method.
+      //
+      // Note that it would be more accurate to inspect the stack trace to locate the origin of the
+      // exception. However, according to the documentation on Throwable#getStackTrace, "some
+      // virtual machines may, under some circumstances, omit one or more stack frames from the
+      // stack trace". "In the extreme case, a virtual machine that has no stack trace information
+      // concerning this throwable is permitted to return a zero-length array from this method."
+      // Therefore, the best we can do is to inspect the message in the exception.
+      // TODO(linyuh): try to avoid the expected failure.
+      String message = exception.getMessage();
+      if (message != null
+          && ((exception instanceof StaleDataException
+                  && message.startsWith("Attempting to access a closed CursorWindow"))
+              || (exception instanceof IllegalStateException
+                  && message.startsWith("attempt to re-open an already-closed object")))) {
+        throw new ExpectedCoalescerException(exception);
+      }
+
+      throw exception;
+    }
   }
 
   /** Combines rows from {@link AnnotatedCallLog} into a {@link CoalescedRow}. */
@@ -337,4 +376,11 @@
       return dialerPhoneNumberUtil.isMatch(groupPhoneNumber, rowPhoneNumber);
     }
   }
+
+  /** A checked exception thrown when expected failure happens when coalescing is in progress. */
+  public static final class ExpectedCoalescerException extends Exception {
+    ExpectedCoalescerException(Throwable throwable) {
+      super("Expected coalescing exception", throwable);
+    }
+  }
 }
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
index 4141fe7..5e72a1a 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
@@ -17,7 +17,6 @@
 
 import android.app.Activity;
 import android.database.Cursor;
-import android.database.StaleDataException;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
@@ -34,6 +33,7 @@
 import com.android.dialer.calllog.CallLogComponent;
 import com.android.dialer.calllog.RefreshAnnotatedCallLogReceiver;
 import com.android.dialer.calllog.database.CallLogDatabaseComponent;
+import com.android.dialer.calllog.database.Coalescer;
 import com.android.dialer.calllog.model.CoalescedRow;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
@@ -337,16 +337,13 @@
           }
         },
         throwable -> {
-          if (throwable instanceof StaleDataException) {
-            // Coalescing can fail if the cursor passed to Coalescer is closed by the loader while
-            // the work is still in progress.
-            // This can happen when the loader restarts and finishes loading data before the
-            // coalescing work is completed.
-            // This failure doesn't need to be thrown as coalescing will be restarted on the latest
-            // data obtained by the loader.
-            // TODO(linyuh): Also throw an exception if the failure above can be avoided.
-            LogUtil.e("NewCallLogFragment.onLoadFinished", "coalescing failed", throwable);
-          } else {
+          // Coalescing can fail if the cursor passed to Coalescer is closed by the loader while
+          // the work is still in progress.
+          // This can happen when the loader restarts and finishes loading data before the
+          // coalescing work is completed.
+          // This failure is identified by ExpectedCoalescerException and doesn't need to be
+          // thrown as coalescing will be restarted on the latest data obtained by the loader.
+          if (!(throwable instanceof Coalescer.ExpectedCoalescerException)) {
             throw new AssertionError(throwable);
           }
         });
diff --git a/java/com/android/dialer/main/impl/OldMainActivityPeer.java b/java/com/android/dialer/main/impl/OldMainActivityPeer.java
index d790c68..73bb4f8 100644
--- a/java/com/android/dialer/main/impl/OldMainActivityPeer.java
+++ b/java/com/android/dialer/main/impl/OldMainActivityPeer.java
@@ -51,6 +51,7 @@
 import android.view.ActionMode;
 import android.view.DragEvent;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.TextView;
 import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
@@ -334,7 +335,12 @@
             activity.findViewById(R.id.remove_view),
             activity.findViewById(R.id.search_view_container),
             toolbar);
-    speedDialFragmentHost = new MainSpeedDialFragmentHost(toolbar);
+    speedDialFragmentHost =
+        new MainSpeedDialFragmentHost(
+            toolbar,
+            activity.findViewById(R.id.root_layout),
+            activity.findViewById(R.id.coordinator_layout),
+            activity.findViewById(R.id.fragment_container));
 
     lastTabController = new LastTabController(activity, bottomNav, showVoicemailTab);
 
@@ -1255,15 +1261,32 @@
   private static final class MainSpeedDialFragmentHost implements SpeedDialFragment.HostInterface {
 
     private final MainToolbar toolbar;
+    private final ViewGroup rootLayout;
+    private final ViewGroup coordinatorLayout;
+    private final ViewGroup fragmentContainer;
 
-    MainSpeedDialFragmentHost(MainToolbar toolbar) {
+    MainSpeedDialFragmentHost(
+        MainToolbar toolbar,
+        ViewGroup rootLayout,
+        ViewGroup coordinatorLayout,
+        ViewGroup fragmentContainer) {
       this.toolbar = toolbar;
+      this.rootLayout = rootLayout;
+      this.coordinatorLayout = coordinatorLayout;
+      this.fragmentContainer = fragmentContainer;
     }
 
     @Override
     public void setHasFrequents(boolean hasFrequents) {
       toolbar.showClearFrequents(hasFrequents);
     }
+
+    @Override
+    public void dragFavorite(boolean start) {
+      rootLayout.setClipChildren(!start);
+      coordinatorLayout.setClipChildren(!start);
+      fragmentContainer.setClipChildren(!start);
+    }
   }
 
   /**
diff --git a/java/com/android/dialer/main/impl/bottomnav/BottomNavItem.java b/java/com/android/dialer/main/impl/bottomnav/BottomNavItem.java
index 4794b88..48cfc81 100644
--- a/java/com/android/dialer/main/impl/bottomnav/BottomNavItem.java
+++ b/java/com/android/dialer/main/impl/bottomnav/BottomNavItem.java
@@ -20,20 +20,18 @@
 import android.content.res.ColorStateList;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.Nullable;
-import android.support.annotation.Px;
 import android.support.annotation.StringRes;
 import android.util.AttributeSet;
 import android.view.View;
-import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
 import android.widget.TextView;
 import com.android.dialer.common.Assert;
 import com.android.dialer.configprovider.ConfigProviderComponent;
 import com.android.dialer.theme.base.ThemeComponent;
 
 /** Navigation item in a bottom nav. */
-final class BottomNavItem extends LinearLayout {
+final class BottomNavItem extends RelativeLayout {
 
   private ImageView image;
   private TextView text;
@@ -57,7 +55,7 @@
     int colorId =
         selected
             ? ThemeComponent.get(getContext()).theme().getColorPrimary()
-            : ThemeComponent.get(getContext()).theme().getTextColorSecondary();
+            : ThemeComponent.get(getContext()).theme().getColorIcon();
     image.setImageTintList(ColorStateList.valueOf(colorId));
     text.setTextColor(colorId);
   }
@@ -87,20 +85,6 @@
       }
       notificationBadge.setVisibility(View.VISIBLE);
       notificationBadge.setText(countString);
-
-      @Px int margin;
-      if (countString.length() == 1) {
-        margin = getContext().getResources().getDimensionPixelSize(R.dimen.badge_margin_length_1);
-      } else if (countString.length() == 2) {
-        margin = getContext().getResources().getDimensionPixelSize(R.dimen.badge_margin_length_2);
-      } else {
-        margin = getContext().getResources().getDimensionPixelSize(R.dimen.badge_margin_length_3);
-      }
-
-      FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) image.getLayoutParams();
-      params.setMarginStart(margin);
-      params.setMarginEnd(margin);
-      image.setLayoutParams(params);
     }
   }
 }
diff --git a/java/com/android/dialer/main/impl/bottomnav/res/drawable/notification_badge.xml b/java/com/android/dialer/main/impl/bottomnav/res/drawable/notification_badge.xml
index 65bc8f9..e02d7c4 100644
--- a/java/com/android/dialer/main/impl/bottomnav/res/drawable/notification_badge.xml
+++ b/java/com/android/dialer/main/impl/bottomnav/res/drawable/notification_badge.xml
@@ -16,8 +16,6 @@
   -->
 <shape
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-  <solid android:color="?android:attr/colorPrimary"/>
-  <corners android:radius="20dp"/>
-  <stroke android:color="?android:attr/colorBackgroundFloating" android:width="2dp"/>
+    android:shape="oval">
+  <solid android:color="@color/dialer_red"/>
 </shape>
\ No newline at end of file
diff --git a/java/com/android/dialer/main/impl/bottomnav/res/layout/bottom_nav_item.xml b/java/com/android/dialer/main/impl/bottomnav/res/layout/bottom_nav_item.xml
index 02874a9..8c0705f 100644
--- a/java/com/android/dialer/main/impl/bottomnav/res/layout/bottom_nav_item.xml
+++ b/java/com/android/dialer/main/impl/bottomnav/res/layout/bottom_nav_item.xml
@@ -16,49 +16,41 @@
   -->
 <com.android.dialer.main.impl.bottomnav.BottomNavItem
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:minWidth="80dp"
+    android:background="?android:attr/selectableItemBackgroundBorderless"
     android:minHeight="56dp"
-    android:gravity="center"
-    android:background="?android:attr/selectableItemBackgroundBorderless">
+    android:minWidth="80dp">
 
-  <FrameLayout
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content">
-
-    <ImageView
-        android:id="@+id/bottom_nav_item_image"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:layout_marginTop="8dp"/>
-
-    <TextView
-        android:id="@+id/notification_badge"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top|end"
-        android:layout_marginTop="2dp"
-        android:paddingStart="6dp"
-        android:paddingEnd="6dp"
-        android:paddingBottom="1dp"
-        android:minHeight="20dp"
-        android:minWidth="20dp"
-        android:gravity="center"
-        android:textSize="12sp"
-        android:textColor="?android:attr/textColorPrimaryInverse"
-        android:background="@drawable/notification_badge"
-        android:fontFamily="sans-serif-medium"
-        android:visibility="invisible"/>
-  </FrameLayout>
+  <ImageView
+      android:id="@+id/bottom_nav_item_image"
+      android:layout_width="24dp"
+      android:layout_height="24dp"
+      android:layout_marginTop="8dp"
+      android:layout_alignParentTop="true"
+      android:layout_centerHorizontal="true"/>
 
   <TextView
+      android:id="@+id/notification_badge"
+      style="@style/Dialer.TextAppearance.OVERLINE"
+      android:layout_width="16dp"
+      android:layout_height="16dp"
+      android:layout_marginTop="2dp"
+      android:layout_marginStart="-8dp"
+      android:layout_alignParentTop="true"
+      android:layout_toEndOf="@id/bottom_nav_item_image"
+      android:background="@drawable/notification_badge"
+      android:gravity="center"
+      android:textColor="?android:attr/colorBackgroundFloating"
+      android:textSize="11dp"
+      android:visibility="invisible"/>
+  <TextView
       android:id="@+id/bottom_nav_item_text"
+      style="@style/Dialer.TextAppearance.SubHeader2"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginBottom="8dp"
-      android:gravity="center_horizontal"
-      android:textSize="12sp"
-      style="@style/Dialer.TextAppearance.Secondary"/>
+      android:layout_below="@id/bottom_nav_item_image"
+      android:layout_centerHorizontal="true"
+      android:gravity="center_horizontal"/>
 </com.android.dialer.main.impl.bottomnav.BottomNavItem>
\ No newline at end of file
diff --git a/java/com/android/dialer/main/impl/bottomnav/res/values/dimens.xml b/java/com/android/dialer/main/impl/bottomnav/res/values/dimens.xml
deleted file mode 100644
index 8fd376b..0000000
--- a/java/com/android/dialer/main/impl/bottomnav/res/values/dimens.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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
-  -->
-<resources>
-  <dimen name="badge_margin_length_1">10dp</dimen>
-  <dimen name="badge_margin_length_2">14dp</dimen>
-  <dimen name="badge_margin_length_3">22dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/java/com/android/dialer/main/impl/res/layout/main_activity.xml b/java/com/android/dialer/main/impl/res/layout/main_activity.xml
index 5cb457e..5fd9bed 100644
--- a/java/com/android/dialer/main/impl/res/layout/main_activity.xml
+++ b/java/com/android/dialer/main/impl/res/layout/main_activity.xml
@@ -19,9 +19,7 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/root_layout"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:clipChildren="false"
-    android:clipToPadding="false">
+    android:layout_height="match_parent">
 
   <!-- MainToolbar -->
   <include
@@ -35,17 +33,13 @@
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:layout_below="@+id/toolbar"
-      android:layout_above="@+id/bottom_nav_bar"
-      android:clipChildren="false"
-      android:clipToPadding="false">
+      android:layout_above="@+id/bottom_nav_bar">
 
     <!-- Holds SpeedDial, Call Log, Contacts, Voicemail and Search fragments -->
     <FrameLayout
         android:id="@+id/fragment_container"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:clipChildren="false"
-        android:clipToPadding="false"/>
+        android:layout_height="match_parent"/>
 
     <FrameLayout
         android:id="@+id/search_fragment_container"
@@ -64,7 +58,7 @@
         android:id="@+id/fab"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_margin="16dp"
+        android:layout_margin="24dp"
         android:layout_gravity="end|bottom"
         android:src="@drawable/quantum_ic_dialpad_white_24"
         android:contentDescription="@string/dialpad_button_content_description"
@@ -102,4 +96,4 @@
         android:importantForAccessibility="no"
         android:visibility="gone"/>
   </FrameLayout>
-</RelativeLayout>
\ No newline at end of file
+</RelativeLayout>
diff --git a/java/com/android/dialer/main/impl/toolbar/res/layout/expanded_search_bar.xml b/java/com/android/dialer/main/impl/toolbar/res/layout/expanded_search_bar.xml
index e5b72f2..9ae884d 100644
--- a/java/com/android/dialer/main/impl/toolbar/res/layout/expanded_search_bar.xml
+++ b/java/com/android/dialer/main/impl/toolbar/res/layout/expanded_search_bar.xml
@@ -29,7 +29,7 @@
       android:background="?attr/selectableItemBackgroundBorderless"
       android:contentDescription="@string/action_menu_back_from_search"
       android:src="@drawable/quantum_ic_arrow_back_vd_theme_24"
-      android:tint="?android:attr/colorPrimary"/>
+      android:tint="?colorIcon"/>
 
   <EditText
       android:id="@+id/search_view"
@@ -45,10 +45,9 @@
       android:inputType="textFilter"
       android:maxLines="1"
       android:hint="@string/dialer_hint_find_contact"
-      android:textColor="?android:attr/textColorSecondary"
       android:textColorHint="?android:attr/textColorHint"
       android:textCursorDrawable="@drawable/custom_cursor"
-      android:textSize="16sp"/>
+      style="@style/Dialer.TextAppearance.Primary"/>
 
   <ImageView
       android:id="@+id/search_clear_button"
diff --git a/java/com/android/dialer/searchfragment/common/res/layout/search_contact_row.xml b/java/com/android/dialer/searchfragment/common/res/layout/search_contact_row.xml
index 4f8e305..08d2baa 100644
--- a/java/com/android/dialer/searchfragment/common/res/layout/search_contact_row.xml
+++ b/java/com/android/dialer/searchfragment/common/res/layout/search_contact_row.xml
@@ -19,8 +19,8 @@
     android:orientation="horizontal"
     android:layout_width="match_parent"
     android:layout_height="@dimen/search_row_height"
-    android:paddingStart="8dp"
-    android:paddingEnd="8dp"
+    android:paddingStart="16dp"
+    android:paddingEnd="16dp"
     android:background="?android:attr/selectableItemBackground">
 
   <QuickContactBadge
@@ -44,7 +44,6 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:gravity="center_vertical|start"
-        android:fontFamily="sans-serif"
         style="@style/Dialer.TextAppearance.Primary"/>
 
     <com.android.dialer.widget.BidiTextView
@@ -52,7 +51,6 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:gravity="center_vertical|start"
-        android:fontFamily="sans-serif"
         style="@style/Dialer.TextAppearance.Secondary"/>
   </LinearLayout>
 
diff --git a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
index 65ce873..809f917 100644
--- a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
+++ b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
@@ -121,11 +121,23 @@
   // the contacts in them).
   private final List<Directory> directories = new ArrayList<>();
   private final Runnable loaderCp2ContactsRunnable =
-      () -> getLoaderManager().restartLoader(CONTACTS_LOADER_ID, null, this);
+      () -> {
+        if (getHost() != null) {
+          getLoaderManager().restartLoader(CONTACTS_LOADER_ID, null, this);
+        }
+      };
   private final Runnable loadNearbyPlacesRunnable =
-      () -> getLoaderManager().restartLoader(NEARBY_PLACES_LOADER_ID, null, this);
+      () -> {
+        if (getHost() != null) {
+          getLoaderManager().restartLoader(NEARBY_PLACES_LOADER_ID, null, this);
+        }
+      };
   private final Runnable loadDirectoryContactsRunnable =
-      () -> getLoaderManager().restartLoader(DIRECTORY_CONTACTS_LOADER_ID, null, this);
+      () -> {
+        if (getHost() != null) {
+          getLoaderManager().restartLoader(DIRECTORY_CONTACTS_LOADER_ID, null, this);
+        }
+      };
   private final Runnable capabilitiesUpdatedRunnable = () -> adapter.notifyDataSetChanged();
 
   private Runnable updatePositionRunnable;
diff --git a/java/com/android/dialer/searchfragment/list/res/layout/header_layout.xml b/java/com/android/dialer/searchfragment/list/res/layout/header_layout.xml
index ff59206..16f3701 100644
--- a/java/com/android/dialer/searchfragment/list/res/layout/header_layout.xml
+++ b/java/com/android/dialer/searchfragment/list/res/layout/header_layout.xml
@@ -27,5 +27,5 @@
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_gravity="center_vertical"
-      style="@style/Dialer.TextAppearance.Secondary.Ellipsize"/>
+      style="@style/Dialer.TextAppearance.OVERLINE.Ellipsize"/>
 </FrameLayout>
diff --git a/java/com/android/dialer/speeddial/SpeedDialAdapter.java b/java/com/android/dialer/speeddial/SpeedDialAdapter.java
index ff52a07..4a48b65 100644
--- a/java/com/android/dialer/speeddial/SpeedDialAdapter.java
+++ b/java/com/android/dialer/speeddial/SpeedDialAdapter.java
@@ -34,6 +34,7 @@
 import com.android.dialer.common.Assert;
 import com.android.dialer.speeddial.FavoritesViewHolder.FavoriteContactsListener;
 import com.android.dialer.speeddial.HeaderViewHolder.SpeedDialHeaderListener;
+import com.android.dialer.speeddial.SpeedDialFragment.HostInterface;
 import com.android.dialer.speeddial.SuggestionViewHolder.SuggestedContactsListener;
 import com.android.dialer.speeddial.draghelper.SpeedDialItemTouchHelperCallback.ItemTouchHelperAdapter;
 import com.android.dialer.speeddial.loader.SpeedDialUiItem;
@@ -81,6 +82,7 @@
   private final FavoriteContactsListener favoritesListener;
   private final SuggestedContactsListener suggestedListener;
   private final SpeedDialHeaderListener headerListener;
+  private final HostInterface hostInterface;
 
   private final Map<Integer, Integer> positionToRowTypeMap = new ArrayMap<>();
   private List<SpeedDialUiItem> speedDialUiItems;
@@ -95,11 +97,13 @@
       Context context,
       FavoriteContactsListener favoritesListener,
       SuggestedContactsListener suggestedListener,
-      SpeedDialHeaderListener headerListener) {
+      SpeedDialHeaderListener headerListener,
+      HostInterface hostInterface) {
     this.context = context;
     this.favoritesListener = favoritesListener;
     this.suggestedListener = suggestedListener;
     this.headerListener = headerListener;
+    this.hostInterface = hostInterface;
   }
 
   @Override
@@ -255,6 +259,7 @@
         if (viewHolder != null) {
           draggingFavoritesViewHolder = (FavoritesViewHolder) viewHolder;
           draggingFavoritesViewHolder.onSelectedChanged(true);
+          hostInterface.dragFavorite(true);
           removeViewHolder.show();
         }
         break;
@@ -263,6 +268,7 @@
         if (draggingFavoritesViewHolder != null) {
           draggingFavoritesViewHolder.onSelectedChanged(false);
           draggingFavoritesViewHolder = null;
+          hostInterface.dragFavorite(false);
           removeViewHolder.hide();
         }
         break;
diff --git a/java/com/android/dialer/speeddial/SpeedDialFragment.java b/java/com/android/dialer/speeddial/SpeedDialFragment.java
index a958d44..d4e7a76 100644
--- a/java/com/android/dialer/speeddial/SpeedDialFragment.java
+++ b/java/com/android/dialer/speeddial/SpeedDialFragment.java
@@ -156,7 +156,12 @@
             new UpdateSpeedDialAdapterListener(),
             speedDialLoaderListener);
     adapter =
-        new SpeedDialAdapter(getContext(), favoritesListener, suggestedListener, headerListener);
+        new SpeedDialAdapter(
+            getContext(),
+            favoritesListener,
+            suggestedListener,
+            headerListener,
+            FragmentUtils.getParentUnsafe(this, HostInterface.class));
     layoutManager.setSpanSizeLookup(adapter.getSpanSizeLookup());
     RecyclerView recyclerView = rootLayout.findViewById(R.id.speed_dial_recycler_view);
     recyclerView.setLayoutManager(layoutManager);
@@ -653,5 +658,7 @@
   public interface HostInterface {
 
     void setHasFrequents(boolean hasFrequents);
+
+    void dragFavorite(boolean start);
   }
 }
diff --git a/java/com/android/dialer/speeddial/res/layout/fragment_speed_dial.xml b/java/com/android/dialer/speeddial/res/layout/fragment_speed_dial.xml
index 472f9e9..e79bc6b 100644
--- a/java/com/android/dialer/speeddial/res/layout/fragment_speed_dial.xml
+++ b/java/com/android/dialer/speeddial/res/layout/fragment_speed_dial.xml
@@ -17,9 +17,7 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/speed_dial_root"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:clipChildren="false"
-    android:clipToPadding="false">
+    android:layout_height="match_parent">
 
   <android.support.v7.widget.RecyclerView
       android:id="@+id/speed_dial_recycler_view"
diff --git a/java/com/android/dialer/theme/base/res/values/theme_dialer_dark.xml b/java/com/android/dialer/theme/base/res/values/theme_dialer_dark.xml
index d03a96b..d2cc0fc 100644
--- a/java/com/android/dialer/theme/base/res/values/theme_dialer_dark.xml
+++ b/java/com/android/dialer/theme/base/res/values/theme_dialer_dark.xml
@@ -51,8 +51,8 @@
     <item name="colorPrimary">@color/dialer_dark_theme_color</item>
     <item name="android:colorPrimaryDark">@color/dialer_dark_theme_color_dark</item>
     <item name="colorPrimaryDark">@color/dialer_dark_theme_color_dark</item>
-    <item name="android:colorAccent">@color/dialer_dark_secondary_color</item>
-    <item name="colorAccent">@color/dialer_dark_secondary_color</item>
+    <item name="android:colorAccent">@color/dialer_dark_theme_color</item>
+    <item name="colorAccent">@color/dialer_dark_theme_color</item>
 
     <!-- Used to automatically style check/selected checkbox, switches and radio buttons -->
     <item name="colorControlActivated">?android:attr/colorPrimary</item>
diff --git a/java/com/android/dialer/theme/base/res/values/theme_dialer_light.xml b/java/com/android/dialer/theme/base/res/values/theme_dialer_light.xml
index 8aa87df..69ecba4 100644
--- a/java/com/android/dialer/theme/base/res/values/theme_dialer_light.xml
+++ b/java/com/android/dialer/theme/base/res/values/theme_dialer_light.xml
@@ -57,8 +57,8 @@
     <item name="colorPrimary">@color/dialer_theme_color</item>
     <item name="android:colorPrimaryDark">@color/dialer_theme_color_dark</item>
     <item name="colorPrimaryDark">@color/dialer_theme_color_dark</item>
-    <item name="android:colorAccent">@color/dialer_secondary_color</item>
-    <item name="colorAccent">@color/dialer_secondary_color</item>
+    <item name="android:colorAccent">@color/dialer_theme_color</item>
+    <item name="colorAccent">@color/dialer_theme_color</item>
 
     <!-- Used to automatically style check/selected checkbox, switches and radio buttons -->
     <item name="colorControlActivated">?android:attr/colorPrimary</item>
diff --git a/java/com/android/dialer/theme/common/res/values/colors.xml b/java/com/android/dialer/theme/common/res/values/colors.xml
index 207d29c..0af550f 100644
--- a/java/com/android/dialer/theme/common/res/values/colors.xml
+++ b/java/com/android/dialer/theme/common/res/values/colors.xml
@@ -27,7 +27,7 @@
   <color name="notification_action_dismiss">#A52714</color>
   <color name="notification_action_answer_video">#097138</color>
 
-  <color name="dialer_red">#C53929</color>
+  <color name="dialer_red">#EA4335</color>
 
   <!-- Legacy -->
   <color name="blue_grey_100">#CFD8DC</color>
diff --git a/java/com/android/dialer/theme/common/res/values/text_styles.xml b/java/com/android/dialer/theme/common/res/values/text_styles.xml
index df5dafa..1f2d6c7 100644
--- a/java/com/android/dialer/theme/common/res/values/text_styles.xml
+++ b/java/com/android/dialer/theme/common/res/values/text_styles.xml
@@ -37,6 +37,12 @@
     <item name="android:fontFamily">sans-serif-medium</item>
   </style>
 
+  <style name="Dialer.TextAppearance.SubHeader2" parent="TextAppearance.AppCompat">
+    <item name="android:textColor">?android:attr/textColorSecondary</item>
+    <item name="android:textSize">14sp</item>
+    <item name="android:fontFamily">sans-serif-medium</item>
+  </style>
+
   <style name="Dialer.TextAppearance.Primary" parent="TextAppearance.AppCompat">
     <item name="android:textColor">?android:attr/textColorPrimary</item>
     <item name="android:textSize">16sp</item>
diff --git a/java/com/android/dialer/theme/hidden/res/values/colors_dialer_dark.xml b/java/com/android/dialer/theme/hidden/res/values/colors_dialer_dark.xml
index e48474e..41c5fd1 100644
--- a/java/com/android/dialer/theme/hidden/res/values/colors_dialer_dark.xml
+++ b/java/com/android/dialer/theme/hidden/res/values/colors_dialer_dark.xml
@@ -18,5 +18,4 @@
   <color name="dialer_dark_theme_color">#5195EA</color>
   <color name="dialer_dark_theme_color_20pct">#335195EA</color>
   <color name="dialer_dark_theme_color_dark">#2374CE</color>
-  <color name="dialer_dark_secondary_color">#5195EA</color>
 </resources>
diff --git a/java/com/android/dialer/theme/hidden/res/values/colors_dialer_light.xml b/java/com/android/dialer/theme/hidden/res/values/colors_dialer_light.xml
index 4621be1..ec2f262 100644
--- a/java/com/android/dialer/theme/hidden/res/values/colors_dialer_light.xml
+++ b/java/com/android/dialer/theme/hidden/res/values/colors_dialer_light.xml
@@ -18,5 +18,4 @@
   <color name="dialer_theme_color">@color/google_blue_600</color>
   <color name="dialer_theme_color_20pct">#332A56C6</color>
   <color name="dialer_theme_color_dark">#1C3AA9</color>
-  <color name="dialer_secondary_color">#F50057</color>
 </resources>
diff --git a/java/com/android/dialer/voicemail/settings/AndroidManifest.xml b/java/com/android/dialer/voicemail/settings/AndroidManifest.xml
index 71a70da..8506ddd 100644
--- a/java/com/android/dialer/voicemail/settings/AndroidManifest.xml
+++ b/java/com/android/dialer/voicemail/settings/AndroidManifest.xml
@@ -13,12 +13,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.dialer.voicemail.settings">
 
   <application>
-
     <!-- Causes the "Voicemail" item under "Calls" setting to be hidden. The voicemail module will
     be handling the settings. Has no effect before OC where dialer cannot provide voicemail
     settings -->
@@ -43,7 +41,13 @@
         android:parentActivityName="com.android.dialer.app.settings.DialerSettingsActivity"
         android:theme="@style/SettingsStyle">
     </activity>
-    <activity android:name=".CurrentVoicemailGreetingActivity">
+    <activity
+        android:name=".CurrentVoicemailGreetingActivity"
+        android:label="@string/voicemail_change_greeting_preference_title"
+        android:parentActivityName="com.android.dialer.app.settings.DialerSettingsActivity"
+        android:exported="true">
+    <!-- TODO(a bug): delete android:exported="true" closer to submitting  -->
     </activity>
   </application>
+
 </manifest>
diff --git a/java/com/android/dialer/voicemail/settings/CurrentVoicemailGreetingActivity.java b/java/com/android/dialer/voicemail/settings/CurrentVoicemailGreetingActivity.java
index 2b6f27b..ff7ae97 100644
--- a/java/com/android/dialer/voicemail/settings/CurrentVoicemailGreetingActivity.java
+++ b/java/com/android/dialer/voicemail/settings/CurrentVoicemailGreetingActivity.java
@@ -16,15 +16,154 @@
 
 package com.android.dialer.voicemail.settings;
 
+import android.Manifest;
 import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.media.MediaPlayer;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.ActivityCompat;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageButton;
+import android.widget.TextView;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.widget.DialerToolbar;
+import java.io.IOException;
+import java.util.Locale;
 
 /** Activity to display current voicemail greeting and allow user to navigate to record a new one */
 public class CurrentVoicemailGreetingActivity extends Activity {
+  public static final String VOICEMAIL_GREETING_FILEPATH_KEY = "canonVoicemailGreetingFilePathKey";
+
+  private static final int REQUEST_RECORD_AUDIO_PERMISSION = 200;
+
+  private boolean permissionToRecordAccepted = false;
+
+  private ImageButton changeGreetingButton;
+  private ImageButton playButton;
+
+  private DialerToolbar currentVoicemailGreetingDialerToolbar;
+
+  private int greetingDuration = -1;
+
+  private MediaPlayer mediaPlayer;
+
+  private TextView playbackProgressLabel;
+  private View playbackDisplay;
+
+  private String voicemailGreetingFilePath = "";
 
   @Override
   protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_current_voicemail_greeting);
+
+    playbackDisplay = findViewById(R.id.current_voicemail_greeting_recording_display);
+    playbackProgressLabel = (TextView) findViewById(R.id.playback_progress_text_view);
+    currentVoicemailGreetingDialerToolbar = (DialerToolbar) findViewById(R.id.toolbar);
+
+    currentVoicemailGreetingDialerToolbar.setTitle(
+        R.string.voicemail_change_greeting_preference_title);
+
+    changeGreetingButton = (ImageButton) findViewById(R.id.change_greeting_button);
+    changeGreetingButton.setOnClickListener(
+        new OnClickListener() {
+          @Override
+          public void onClick(View v) {
+            // TODO(sabowitz): Implement this in CL child beta01.
+          }
+        });
+
+    playButton = (ImageButton) findViewById(R.id.play_button);
+    playButton.setOnClickListener(
+        new OnClickListener() {
+          @Override
+          public void onClick(View v) {
+            // TODO(sabowitz): Finish implementing this in CL child beta02.
+          }
+        });
+
+    displayCurrentVoicemailGreetingStatus();
+  }
+
+  @Override
+  public void onStart() {
+    ActivityCompat.requestPermissions(
+        this, new String[] {Manifest.permission.RECORD_AUDIO}, REQUEST_RECORD_AUDIO_PERMISSION);
+
+    if (isGreetingRecorded()) {
+      mediaPlayer = new MediaPlayer();
+      try {
+        mediaPlayer.setDataSource(voicemailGreetingFilePath);
+        mediaPlayer.prepare();
+      } catch (IOException e) {
+        LogUtil.e("CurrentVoicemailGreetingActivity.onStart", "mediaPlayer setup failed.");
+      }
+    }
+    super.onStart();
+  }
+
+  @Override
+  public void onPause() {
+    if (isGreetingRecorded()) {
+      if (mediaPlayer.isPlaying()) {
+        mediaPlayer.release();
+        mediaPlayer = null;
+      }
+    }
+    super.onPause();
+  }
+
+  @Override
+  public void onRequestPermissionsResult(
+      int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+
+    if (requestCode == REQUEST_RECORD_AUDIO_PERMISSION) {
+      permissionToRecordAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
+    }
+    if (!permissionToRecordAccepted) {
+      LogUtil.w(
+          "CurrentVoicemailGreetingActivity.onRequestPermissionsResult",
+          "permissionToRecordAccepted = false.");
+      // TODO(sabowitz): Implement error dialog logic in a child CL.
+    }
+  }
+
+  private boolean isGreetingRecorded() {
+    Intent intent = getIntent();
+    if (intent.hasExtra(VOICEMAIL_GREETING_FILEPATH_KEY)) {
+      String filePathProxy = intent.getStringExtra(VOICEMAIL_GREETING_FILEPATH_KEY);
+      if (filePathProxy == null || filePathProxy.length() == 0) {
+        return false;
+      }
+      if (mediaPlayer == null) {
+        mediaPlayer = new MediaPlayer();
+      }
+      try {
+        mediaPlayer.setDataSource(filePathProxy);
+        int durationProxy = mediaPlayer.getDuration();
+        greetingDuration = durationProxy;
+        voicemailGreetingFilePath = filePathProxy;
+        mediaPlayer = null;
+        return true;
+      } catch (IOException e) {
+        LogUtil.e("CurrentVoicemailGreetingActivity.isGreetingRecorded", "bad filepath.");
+        mediaPlayer = null;
+        return false;
+      }
+    }
+    return false;
+  }
+
+  private void displayCurrentVoicemailGreetingStatus() {
+    if (isGreetingRecorded()) {
+      String durationLabel = String.format(Locale.US, "00:%d", greetingDuration);
+      playbackProgressLabel.setText(durationLabel);
+    } else {
+      playbackDisplay.setVisibility(View.GONE);
+    }
   }
 }
diff --git a/java/com/android/dialer/voicemail/settings/res/drawable-xhdpi/ic_arrow_back_grey600_48dp.png b/java/com/android/dialer/voicemail/settings/res/drawable-xhdpi/ic_arrow_back_grey600_48dp.png
new file mode 100644
index 0000000..0a379b4
--- /dev/null
+++ b/java/com/android/dialer/voicemail/settings/res/drawable-xhdpi/ic_arrow_back_grey600_48dp.png
Binary files differ
diff --git a/java/com/android/dialer/voicemail/settings/res/drawable-xhdpi/ic_circles_add_googblue_48dp.png b/java/com/android/dialer/voicemail/settings/res/drawable-xhdpi/ic_circles_add_googblue_48dp.png
new file mode 100644
index 0000000..5ae5230
--- /dev/null
+++ b/java/com/android/dialer/voicemail/settings/res/drawable-xhdpi/ic_circles_add_googblue_48dp.png
Binary files differ
diff --git a/java/com/android/dialer/voicemail/settings/res/drawable-xhdpi/ic_play_circle_filled_googblue_48dp.png b/java/com/android/dialer/voicemail/settings/res/drawable-xhdpi/ic_play_circle_filled_googblue_48dp.png
new file mode 100644
index 0000000..0657758
--- /dev/null
+++ b/java/com/android/dialer/voicemail/settings/res/drawable-xhdpi/ic_play_circle_filled_googblue_48dp.png
Binary files differ
diff --git a/java/com/android/dialer/voicemail/settings/res/drawable-xhdpi/ic_stop_circle_filled_blue_24dp.png b/java/com/android/dialer/voicemail/settings/res/drawable-xhdpi/ic_stop_circle_filled_blue_24dp.png
new file mode 100644
index 0000000..520f860
--- /dev/null
+++ b/java/com/android/dialer/voicemail/settings/res/drawable-xhdpi/ic_stop_circle_filled_blue_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/voicemail/settings/res/layout/activity_current_voicemail_greeting.xml b/java/com/android/dialer/voicemail/settings/res/layout/activity_current_voicemail_greeting.xml
index 81d175a..3e90cf7 100644
--- a/java/com/android/dialer/voicemail/settings/res/layout/activity_current_voicemail_greeting.xml
+++ b/java/com/android/dialer/voicemail/settings/res/layout/activity_current_voicemail_greeting.xml
@@ -21,4 +21,107 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     tools:context="com.android.dialer.voicemail.settings.CurrentVoicemailGreetingActivity">
+  <LinearLayout
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:layout_alignParentTop="true"
+      android:layout_centerHorizontal="true"
+      android:orientation="vertical">
+    <com.android.dialer.widget.DialerToolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:title="@string/voicemail_change_greeting_preference_title">
+    </com.android.dialer.widget.DialerToolbar>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="@color/dialer_divider_line_color"
+        />
+
+    <!-- Change Greeting Panel -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="72dp"
+        android:layout_marginTop="8dp"
+        android:layout_marginBottom="7dp"
+        android:orientation="horizontal"
+        android:visibility="visible">
+
+        <!-- Change Greeting Button -->
+      <ImageButton
+          android:id="@+id/change_greeting_button"
+          style="@android:style/Widget.Material.Light.ImageButton"
+          android:layout_width="40dp"
+          android:layout_height="40dp"
+          android:layout_marginStart="16dp"
+          android:layout_gravity="center_vertical"
+          android:background="?android:attr/colorBackground"
+          android:foregroundGravity="center_vertical"
+          android:scaleType="centerCrop"
+          android:src="@drawable/ic_circles_add_googblue_48dp"/>
+
+      <TextView
+          android:id="@+id/textView"
+          android:layout_width="0dp"
+          android:layout_height="wrap_content"
+          android:layout_weight="1"
+          android:layout_marginStart="17dp"
+          android:layout_gravity="center_vertical"
+          android:text="@string/current_voicemail_greeting_record_instruction"
+          style="@style/Dialer.TextAppearance.Primary"/>
+
+    </LinearLayout>
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="@color/dialer_divider_line_color"
+        />
+    <LinearLayout
+        android:id="@+id/current_voicemail_greeting_recording_display"
+        android:layout_width="match_parent"
+        android:layout_height="72dp"
+        android:background="?android:attr/colorBackground"
+        android:orientation="horizontal">
+        <!-- Play Button -->
+      <ImageButton
+          android:id="@+id/play_button"
+          style="@android:style/Widget.Material.Light.ImageButton"
+          android:layout_width="40dp"
+          android:layout_height="40dp"
+          android:layout_marginStart="16dp"
+          android:layout_gravity="center_vertical"
+          android:adjustViewBounds="false"
+          android:background="?android:attr/colorBackground"
+          android:cropToPadding="false"
+          android:scaleType="centerCrop"
+          android:src="@drawable/ic_play_circle_filled_googblue_48dp"/>
+
+      <LinearLayout
+          android:layout_width="232dp"
+          android:layout_height="wrap_content"
+          android:layout_weight="1"
+          android:layout_marginStart="16dp"
+          android:layout_gravity="center_vertical"
+          android:orientation="vertical">
+        <TextView
+            android:id="@+id/current_voicemail_greeting_recording_title_textview"
+            style="@style/Dialer.TextAppearance.Primary"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/current_voicemail_greeting_recording_title"/>
+        <TextView
+            android:id="@+id/playback_progress_text_view"
+            style="@style/Dialer.TextAppearance.Secondary"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"/>
+      </LinearLayout>
+    </LinearLayout>
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="@color/dialer_divider_line_color"
+        />
+  </LinearLayout>
 </RelativeLayout>
diff --git a/java/com/android/dialer/voicemail/settings/res/values/strings.xml b/java/com/android/dialer/voicemail/settings/res/values/strings.xml
index 14793fd..971f37a 100644
--- a/java/com/android/dialer/voicemail/settings/res/values/strings.xml
+++ b/java/com/android/dialer/voicemail/settings/res/values/strings.xml
@@ -36,6 +36,12 @@
   <!-- Title for changing voicemail greeting activity [CHAR LIMIT=40] -->
   <string name="voicemail_change_greeting_preference_title">Voicemail greeting</string>
 
+  <!-- Text instruction shown on the current voicemail greeting display screen [CHAR LIMIT=20] -->
+  <string name="current_voicemail_greeting_record_instruction">Record a greeting</string>
+
+  <!-- Title for voicemail greeting, if one is already recorded. -->
+  <string name="current_voicemail_greeting_recording_title">My voicemail greeting</string>
+
   <!-- Text shown on the record voicemail activity [CHAR LIMIT=20] -->
   <string name="change_greeting_text">Tap to Record</string>