Merge changes If9478ce2,I41d4910b
am: 9acd105472

Change-Id: I2a7baeace7ad935ae8294345e829acc9ff7e271f
diff --git a/java/com/android/dialer/app/DialtactsActivity.java b/java/com/android/dialer/app/DialtactsActivity.java
index 755b335..2d82b6f 100644
--- a/java/com/android/dialer/app/DialtactsActivity.java
+++ b/java/com/android/dialer/app/DialtactsActivity.java
@@ -491,6 +491,11 @@
     mP13nLogger = P13nLogging.get(getApplicationContext());
     mP13nRanker = P13nRanking.get(getApplicationContext());
     Trace.endSection();
+
+    // Update the new search fragment to the correct position and the ActionBar's visibility.
+    if (ConfigProviderBindings.get(this).getBoolean("enable_new_search_fragment", false)) {
+      updateSearchFragmentPosition();
+    }
   }
 
   @NonNull
@@ -1550,6 +1555,10 @@
 
   @Override
   public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+    // FAB does not move with the new favorites UI
+    if (newFavoritesIsEnabled()) {
+      return;
+    }
     int tabIndex = mListsFragment.getCurrentTabIndex();
 
     // Scroll the button from center to end when moving from the Speed Dial to Call History tab.
@@ -1610,7 +1619,8 @@
 
   @VisibleForTesting
   public int getFabAlignment() {
-    if (!mIsLandscape
+    if (!newFavoritesIsEnabled()
+        && !mIsLandscape
         && !isInSearchUi()
         && mListsFragment.getCurrentTabIndex() == DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL) {
       return FloatingActionButtonController.ALIGN_MIDDLE;
@@ -1741,4 +1751,8 @@
   static void setVoiceSearchEnabledForTest(Optional<Boolean> enabled) {
     sVoiceSearchEnabledForTest = enabled;
   }
+
+  private boolean newFavoritesIsEnabled() {
+    return ConfigProviderBindings.get(this).getBoolean("enable_new_favorites_tab", false);
+  }
 }
diff --git a/java/com/android/dialer/speeddial/FavoritesViewHolder.java b/java/com/android/dialer/speeddial/FavoritesViewHolder.java
new file mode 100644
index 0000000..0cde716
--- /dev/null
+++ b/java/com/android/dialer/speeddial/FavoritesViewHolder.java
@@ -0,0 +1,121 @@
+/*
+ * 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.speeddial;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.widget.FrameLayout;
+import android.widget.QuickContactBadge;
+import android.widget.TextView;
+import com.android.dialer.common.Assert;
+import com.android.dialer.contactphoto.ContactPhotoManager;
+import com.android.dialer.lettertile.LetterTileDrawable;
+
+/** ViewHolder for starred/favorite contacts in {@link SpeedDialFragment}. */
+public class FavoritesViewHolder extends RecyclerView.ViewHolder
+    implements OnClickListener, OnLongClickListener {
+
+  private final FavoriteContactsListener listener;
+
+  private final QuickContactBadge photoView;
+  private final TextView nameView;
+  private final TextView phoneType;
+  private final FrameLayout videoCallIcon;
+
+  private boolean isVideoCall;
+  private String number;
+
+  public FavoritesViewHolder(View view, FavoriteContactsListener listener) {
+    super(view);
+    photoView = view.findViewById(R.id.avatar);
+    nameView = view.findViewById(R.id.name);
+    phoneType = view.findViewById(R.id.phone_type);
+    videoCallIcon = view.findViewById(R.id.video_call_container);
+    view.setOnClickListener(this);
+    view.setOnLongClickListener(this);
+    photoView.setClickable(false);
+    this.listener = listener;
+  }
+
+  public void bind(Context context, Cursor cursor) {
+    Assert.checkArgument(cursor.getInt(StrequentContactsCursorLoader.PHONE_STARRED) == 1);
+    isVideoCall = false; // TODO(calderwoodra): get from disambig data
+    number = cursor.getString(StrequentContactsCursorLoader.PHONE_NUMBER);
+
+    String name = cursor.getString(StrequentContactsCursorLoader.PHONE_DISPLAY_NAME);
+    long contactId = cursor.getLong(StrequentContactsCursorLoader.PHONE_ID);
+    String lookupKey = cursor.getString(StrequentContactsCursorLoader.PHONE_LOOKUP_KEY);
+    Uri contactUri = Contacts.getLookupUri(contactId, lookupKey);
+
+    String photoUri = cursor.getString(StrequentContactsCursorLoader.PHONE_PHOTO_URI);
+    ContactPhotoManager.getInstance(context)
+        .loadDialerThumbnailOrPhoto(
+            photoView,
+            contactUri,
+            cursor.getLong(StrequentContactsCursorLoader.PHONE_PHOTO_ID),
+            photoUri == null ? null : Uri.parse(photoUri),
+            name,
+            LetterTileDrawable.TYPE_DEFAULT);
+    nameView.setText(name);
+    phoneType.setText(getLabel(context.getResources(), cursor));
+    videoCallIcon.setVisibility(isVideoCall ? View.VISIBLE : View.GONE);
+  }
+
+  // TODO(calderwoodra): handle CNAP and cequint types.
+  // TODO(calderwoodra): unify this into a utility method with CallLogAdapter#getNumberType
+  private static String getLabel(Resources resources, Cursor cursor) {
+    int numberType = cursor.getInt(StrequentContactsCursorLoader.PHONE_TYPE);
+    String numberLabel = cursor.getString(StrequentContactsCursorLoader.PHONE_LABEL);
+
+    // Returns empty label instead of "custom" if the custom label is empty.
+    if (numberType == Phone.TYPE_CUSTOM && TextUtils.isEmpty(numberLabel)) {
+      return "";
+    }
+    return (String) Phone.getTypeLabel(resources, numberType, numberLabel);
+  }
+
+  @Override
+  public void onClick(View v) {
+    listener.onClick(number, isVideoCall);
+  }
+
+  @Override
+  public boolean onLongClick(View v) {
+    // TODO(calderwoodra): implement drag and drop logic
+    listener.onLongClick(number);
+    return true;
+  }
+
+  /** Listener/callback for {@link FavoritesViewHolder} actions. */
+  public interface FavoriteContactsListener {
+
+    /** Called when the user clicks on a favorite contact. */
+    void onClick(String number, boolean isVideoCall);
+
+    /** Called when the user long clicks on a favorite contact. */
+    void onLongClick(String number);
+  }
+}
diff --git a/java/com/android/dialer/speeddial/HeaderViewHolder.java b/java/com/android/dialer/speeddial/HeaderViewHolder.java
new file mode 100644
index 0000000..58120ee
--- /dev/null
+++ b/java/com/android/dialer/speeddial/HeaderViewHolder.java
@@ -0,0 +1,60 @@
+/*
+ * 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.speeddial;
+
+import android.support.annotation.StringRes;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+
+/** ViewHolder for headers in {@link SpeedDialFragment}. */
+public class HeaderViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
+
+  private final SpeedDialHeaderListener listener;
+  private final TextView headerText;
+  private final Button addButton;
+
+  public HeaderViewHolder(View view, SpeedDialHeaderListener listener) {
+    super(view);
+    this.listener = listener;
+    headerText = view.findViewById(R.id.speed_dial_header_text);
+    addButton = view.findViewById(R.id.speed_dial_add_button);
+    addButton.setOnClickListener(this);
+  }
+
+  public void setHeaderText(@StringRes int header) {
+    headerText.setText(header);
+  }
+
+  public void showAddButton(boolean show) {
+    addButton.setVisibility(show ? View.VISIBLE : View.GONE);
+  }
+
+  @Override
+  public void onClick(View v) {
+    listener.onAddFavoriteClicked();
+  }
+
+  /** Listener/Callback for {@link HeaderViewHolder} parents. */
+  public interface SpeedDialHeaderListener {
+
+    /** Called when the user wants to add a contact to their favorites. */
+    void onAddFavoriteClicked();
+  }
+}
diff --git a/java/com/android/dialer/speeddial/SpeedDialAdapter.java b/java/com/android/dialer/speeddial/SpeedDialAdapter.java
new file mode 100644
index 0000000..5f7b68e
--- /dev/null
+++ b/java/com/android/dialer/speeddial/SpeedDialAdapter.java
@@ -0,0 +1,141 @@
+/*
+ * 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.speeddial;
+
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.GridLayoutManager.SpanSizeLookup;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.LayoutManager;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+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.SpeedDialCursor.RowType;
+import com.android.dialer.speeddial.SuggestionViewHolder.SuggestedContactsListener;
+
+/**
+ * RecyclerView adapter for {@link SpeedDialFragment}.
+ *
+ * <p>Displays a list in the following order:
+ *
+ * <ol>
+ *   <li>Favorite contacts header (with add button)
+ *   <li>Favorite contacts
+ *   <li>Suggested contacts header
+ *   <li>Suggested contacts
+ * </ol>
+ */
+final class SpeedDialAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+
+  private final Context context;
+  private final FavoriteContactsListener favoritesListener;
+  private final SuggestedContactsListener suggestedListener;
+  private final SpeedDialHeaderListener headerListener;
+
+  private SpeedDialCursor cursor;
+
+  public SpeedDialAdapter(
+      Context context,
+      FavoriteContactsListener favoritesListener,
+      SuggestedContactsListener suggestedListener,
+      SpeedDialHeaderListener headerListener) {
+    this.context = context;
+    this.favoritesListener = favoritesListener;
+    this.suggestedListener = suggestedListener;
+    this.headerListener = headerListener;
+  }
+
+  @Override
+  public int getItemViewType(int position) {
+    return cursor.getRowType(position);
+  }
+
+  @Override
+  public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+    LayoutInflater inflater = LayoutInflater.from(context);
+    if (viewType == RowType.STARRED) {
+      return new FavoritesViewHolder(
+          inflater.inflate(R.layout.favorite_item_layout, parent, false), favoritesListener);
+    } else if (viewType == RowType.SUGGESTION) {
+      return new SuggestionViewHolder(
+          inflater.inflate(R.layout.suggestion_row_layout, parent, false), suggestedListener);
+    } else if (viewType == RowType.HEADER) {
+      return new HeaderViewHolder(
+          inflater.inflate(R.layout.speed_dial_header_layout, parent, false), headerListener);
+    } else {
+      throw Assert.createIllegalStateFailException("Invalid viewType: " + viewType);
+    }
+  }
+
+  @Override
+  public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+    cursor.moveToPosition(position);
+    switch (cursor.getRowType(position)) {
+      case RowType.HEADER:
+        ((HeaderViewHolder) holder).setHeaderText(cursor.getHeader());
+        ((HeaderViewHolder) holder).showAddButton(cursor.hasFavorites() && position == 0);
+        break;
+      case RowType.STARRED:
+        ((FavoritesViewHolder) holder).bind(context, cursor);
+        break;
+      case RowType.SUGGESTION:
+        ((SuggestionViewHolder) holder).bind(context, cursor);
+        break;
+      default:
+        throw Assert.createIllegalStateFailException("Invalid view holder: " + holder);
+    }
+  }
+
+  @Override
+  public int getItemCount() {
+    return cursor == null || cursor.isClosed() ? 0 : cursor.getCount();
+  }
+
+  public void setCursor(SpeedDialCursor cursor) {
+    this.cursor = cursor;
+    notifyDataSetChanged();
+  }
+
+  LayoutManager getLayoutManager(Context context) {
+    GridLayoutManager layoutManager = new GridLayoutManager(context, 3 /* spanCount */);
+    layoutManager.setSpanSizeLookup(
+        new SpanSizeLookup() {
+          @Override
+          public int getSpanSize(int position) {
+            return SpeedDialAdapter.this.getSpanSize(position);
+          }
+        });
+    return layoutManager;
+  }
+
+  @VisibleForTesting
+  int getSpanSize(int position) {
+    switch (cursor.getRowType(position)) {
+      case RowType.SUGGESTION:
+      case RowType.HEADER:
+        return 3; // span the whole screen
+      case RowType.STARRED:
+        return 1; // span 1/3 of the screen
+      default:
+        throw Assert.createIllegalStateFailException(
+            "Invalid row type: " + cursor.getRowType(position));
+    }
+  }
+}
diff --git a/java/com/android/dialer/speeddial/SpeedDialCursor.java b/java/com/android/dialer/speeddial/SpeedDialCursor.java
new file mode 100644
index 0000000..552fab1
--- /dev/null
+++ b/java/com/android/dialer/speeddial/SpeedDialCursor.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.speeddial;
+
+import android.annotation.SuppressLint;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MergeCursor;
+import android.support.annotation.IntDef;
+import android.support.annotation.StringRes;
+import com.android.dialer.common.Assert;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Cursor for favorites contacts. */
+final class SpeedDialCursor extends MergeCursor {
+
+  /**
+   * Caps the speed dial list to contain at most 20 contacts, including favorites and suggestions.
+   * It is only a soft limit though, for the case that there are more than 20 favorite contacts.
+   */
+  private static final int SPEED_DIAL_CONTACT_LIST_SOFT_LIMIT = 20;
+
+  private static final String[] HEADER_CURSOR_PROJECTION = {"header"};
+  private static final int HEADER_COLUMN_POSITION = 0;
+  private boolean hasFavorites;
+
+  @Retention(RetentionPolicy.SOURCE)
+  @IntDef({RowType.HEADER, RowType.STARRED, RowType.SUGGESTION})
+  @interface RowType {
+    int HEADER = 0;
+    int STARRED = 1;
+    int SUGGESTION = 2;
+  }
+
+  public static SpeedDialCursor newInstance(Cursor strequentCursor) {
+    if (strequentCursor == null || strequentCursor.getCount() == 0) {
+      return null;
+    }
+    SpeedDialCursor cursor = new SpeedDialCursor(buildCursors(strequentCursor));
+    strequentCursor.close();
+    return cursor;
+  }
+
+  private static Cursor[] buildCursors(Cursor strequentCursor) {
+    MatrixCursor starred = new MatrixCursor(StrequentContactsCursorLoader.PHONE_PROJECTION);
+    MatrixCursor suggestions = new MatrixCursor(StrequentContactsCursorLoader.PHONE_PROJECTION);
+
+    strequentCursor.moveToPosition(-1);
+    while (strequentCursor.moveToNext()) {
+      if (strequentCursor.getInt(StrequentContactsCursorLoader.PHONE_IS_SUPER_PRIMARY) != 0) {
+        continue;
+      }
+
+      if (strequentCursor.getPosition() != 0) {
+        long contactId = strequentCursor.getLong(StrequentContactsCursorLoader.PHONE_CONTACT_ID);
+        int position = strequentCursor.getPosition();
+        boolean duplicate = false;
+        // Iterate backwards through the cursor to check that this isn't a duplicate contact
+        // TODO(calderwoodra): improve this algorithm (currently O(n^2)).
+        while (strequentCursor.moveToPrevious() && !duplicate) {
+          duplicate |=
+              strequentCursor.getLong(StrequentContactsCursorLoader.PHONE_CONTACT_ID) == contactId;
+        }
+        strequentCursor.moveToPosition(position);
+        if (duplicate) {
+          continue;
+        }
+      }
+
+      if (strequentCursor.getInt(StrequentContactsCursorLoader.PHONE_STARRED) == 1) {
+        StrequentContactsCursorLoader.addToCursor(starred, strequentCursor);
+      } else if (starred.getCount() + suggestions.getCount() < SPEED_DIAL_CONTACT_LIST_SOFT_LIMIT) {
+        // Since all starred contacts come before each non-starred contact, it's safe to assume that
+        // this list will never exceed the soft limit unless there are more starred contacts than
+        // the limit permits.
+        StrequentContactsCursorLoader.addToCursor(suggestions, strequentCursor);
+      }
+    }
+
+    List<Cursor> cursorList = new ArrayList<>();
+    if (starred.getCount() > 0) {
+      cursorList.add(createHeaderCursor(R.string.favorites_header));
+      cursorList.add(starred);
+    }
+    if (suggestions.getCount() > 0) {
+      cursorList.add(createHeaderCursor(R.string.suggestions_header));
+      cursorList.add(suggestions);
+    }
+    return cursorList.toArray(new Cursor[cursorList.size()]);
+  }
+
+  private static Cursor createHeaderCursor(@StringRes int header) {
+    MatrixCursor cursor = new MatrixCursor(HEADER_CURSOR_PROJECTION);
+    cursor.newRow().add(HEADER_CURSOR_PROJECTION[HEADER_COLUMN_POSITION], header);
+    return cursor;
+  }
+
+  @RowType
+  int getRowType(int position) {
+    moveToPosition(position);
+    if (getColumnCount() == 1) {
+      return RowType.HEADER;
+    } else if (getInt(StrequentContactsCursorLoader.PHONE_STARRED) == 1) {
+      return RowType.STARRED;
+    } else {
+      return RowType.SUGGESTION;
+    }
+  }
+
+  @SuppressLint("DefaultLocale")
+  @StringRes
+  int getHeader() {
+    if (getRowType(getPosition()) != RowType.HEADER) {
+      throw Assert.createIllegalStateFailException(
+          String.format("Current position (%d) is not a header.", getPosition()));
+    }
+    return getInt(HEADER_COLUMN_POSITION);
+  }
+
+  public boolean hasFavorites() {
+    return hasFavorites;
+  }
+
+  private SpeedDialCursor(Cursor[] cursors) {
+    super(cursors);
+    for (Cursor cursor : cursors) {
+      cursor.moveToFirst();
+      if (cursor.getColumnCount() != 1
+          && cursor.getInt(StrequentContactsCursorLoader.PHONE_STARRED) == 1) {
+        hasFavorites = true;
+        break;
+      }
+    }
+  }
+}
diff --git a/java/com/android/dialer/speeddial/SpeedDialFragment.java b/java/com/android/dialer/speeddial/SpeedDialFragment.java
index c087439..65e542c 100644
--- a/java/com/android/dialer/speeddial/SpeedDialFragment.java
+++ b/java/com/android/dialer/speeddial/SpeedDialFragment.java
@@ -17,15 +17,37 @@
 package com.android.dialer.speeddial;
 
 import android.app.Fragment;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Loader;
+import android.database.Cursor;
 import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
 import android.support.annotation.Nullable;
+import android.support.v7.widget.RecyclerView;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import com.android.dialer.callintent.CallInitiationType;
+import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.common.Assert;
+import com.android.dialer.precall.PreCall;
+import com.android.dialer.speeddial.FavoritesViewHolder.FavoriteContactsListener;
+import com.android.dialer.speeddial.HeaderViewHolder.SpeedDialHeaderListener;
+import com.android.dialer.speeddial.SuggestionViewHolder.SuggestedContactsListener;
 
 /** Favorites fragment. Contents TBD. TODO(calderwoodra) */
 public class SpeedDialFragment extends Fragment {
 
+  private static final int STREQUENT_CONTACTS_LOADER_ID = 1;
+
+  private final SpeedDialHeaderListener headerListener = new SpeedDialFragmentHeaderListener();
+  private final FavoriteContactsListener favoritesListener = new SpeedDialFavoritesListener();
+  private final SuggestedContactsListener suggestedListener = new SpeedDialSuggestedListener();
+  private final SpeedDialFragmentLoaderCallback loaderCallback =
+      new SpeedDialFragmentLoaderCallback();
+
+  private SpeedDialAdapter adapter;
+
   public static SpeedDialFragment newInstance() {
     return new SpeedDialFragment();
   }
@@ -34,11 +56,106 @@
   @Override
   public View onCreateView(
       LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-    return inflater.inflate(R.layout.fragment_speed_dial, container, false);
+    View view = inflater.inflate(R.layout.fragment_speed_dial, container, false);
+    RecyclerView recyclerView = view.findViewById(R.id.speed_dial_recycler_view);
+
+    adapter =
+        new SpeedDialAdapter(getContext(), favoritesListener, suggestedListener, headerListener);
+    recyclerView.setLayoutManager(adapter.getLayoutManager(getContext()));
+    recyclerView.setAdapter(adapter);
+    getLoaderManager().initLoader(STREQUENT_CONTACTS_LOADER_ID, null /* args */, loaderCallback);
+    return view;
   }
 
   public boolean hasFrequents() {
     // TODO(calderwoodra)
     return false;
   }
+
+  @Override
+  public void onPause() {
+    super.onPause();
+    loaderCallback.unregisterContentObserver();
+  }
+
+  private static class SpeedDialFragmentHeaderListener implements SpeedDialHeaderListener {
+
+    @Override
+    public void onAddFavoriteClicked() {
+      // TODO(calderwoodra): implement add favorite screen
+    }
+  }
+
+  private class SpeedDialFavoritesListener implements FavoriteContactsListener {
+
+    @Override
+    public void onClick(String number, boolean isVideoCall) {
+      // TODO(calderwoodra): add logic for duo video calls
+      PreCall.start(
+          getContext(),
+          new CallIntentBuilder(number, CallInitiationType.Type.SPEED_DIAL)
+              .setIsVideoCall(isVideoCall));
+    }
+
+    @Override
+    public void onLongClick(String number) {
+      // TODO(calderwoodra): show favorite contact floating context menu
+    }
+  }
+
+  private class SpeedDialSuggestedListener implements SuggestedContactsListener {
+
+    @Override
+    public void onOverFlowMenuClicked(String number) {
+      // TODO(calderwoodra) show overflow menu for suggested contacts
+    }
+
+    @Override
+    public void onRowClicked(String number) {
+      PreCall.start(
+          getContext(), new CallIntentBuilder(number, CallInitiationType.Type.SPEED_DIAL));
+    }
+  }
+
+  /**
+   * Loader callback that registers a content observer. {@link #unregisterContentObserver()} needs
+   * to be called during tear down of the fragment.
+   */
+  private class SpeedDialFragmentLoaderCallback implements LoaderCallbacks<Cursor> {
+
+    private StrequentContactsCursorLoader cursorLoader;
+
+    @Override
+    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+      if (id == STREQUENT_CONTACTS_LOADER_ID) {
+        return new StrequentContactsCursorLoader(getContext());
+      }
+      throw Assert.createIllegalStateFailException("Invalid loader id: " + id);
+    }
+
+    @Override
+    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+      cursorLoader = (StrequentContactsCursorLoader) loader;
+      // Since the original cursor we queried against was modified and closed, we need to register a
+      // new content observer in order to get updates on changes to our contacts.
+      getContext()
+          .getContentResolver()
+          .registerContentObserver(
+              Contacts.CONTENT_STREQUENT_URI,
+              true /* notifyForDescendants*/,
+              cursorLoader.getContentObserver());
+      adapter.setCursor((SpeedDialCursor) data);
+    }
+
+    public void unregisterContentObserver() {
+      getContext()
+          .getContentResolver()
+          .unregisterContentObserver(cursorLoader.getContentObserver());
+    }
+
+    @Override
+    public void onLoaderReset(Loader<Cursor> loader) {
+      adapter.setCursor(null);
+    }
+  }
 }
diff --git a/java/com/android/dialer/speeddial/SquareImageView.java b/java/com/android/dialer/speeddial/SquareImageView.java
new file mode 100644
index 0000000..a12f4d4
--- /dev/null
+++ b/java/com/android/dialer/speeddial/SquareImageView.java
@@ -0,0 +1,36 @@
+/*
+ * 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.speeddial;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.widget.QuickContactBadge;
+
+/** A square {@link android.widget.ImageView} constrained on width. */
+public class SquareImageView extends QuickContactBadge {
+
+  public SquareImageView(Context context, @Nullable AttributeSet attrs) {
+    super(context, attrs);
+    setClickable(false);
+  }
+
+  @Override
+  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+    super.onMeasure(widthMeasureSpec, widthMeasureSpec);
+  }
+}
diff --git a/java/com/android/dialer/speeddial/StrequentContactsCursorLoader.java b/java/com/android/dialer/speeddial/StrequentContactsCursorLoader.java
new file mode 100644
index 0000000..f5f0045
--- /dev/null
+++ b/java/com/android/dialer/speeddial/StrequentContactsCursorLoader.java
@@ -0,0 +1,104 @@
+/*
+ * 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.speeddial;
+
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+
+/** Cursor Loader for strequent contacts. */
+final class StrequentContactsCursorLoader extends CursorLoader {
+
+  static final int PHONE_ID = 0;
+  static final int PHONE_DISPLAY_NAME = 1;
+  static final int PHONE_STARRED = 2;
+  static final int PHONE_PHOTO_URI = 3;
+  static final int PHONE_LOOKUP_KEY = 4;
+  static final int PHONE_PHOTO_ID = 5;
+  static final int PHONE_NUMBER = 6;
+  static final int PHONE_TYPE = 7;
+  static final int PHONE_LABEL = 8;
+  static final int PHONE_IS_SUPER_PRIMARY = 9;
+  static final int PHONE_PINNED = 10;
+  static final int PHONE_CONTACT_ID = 11;
+
+  static final String[] PHONE_PROJECTION =
+      new String[] {
+        Phone._ID, // 0
+        Phone.DISPLAY_NAME, // 1
+        Phone.STARRED, // 2
+        Phone.PHOTO_URI, // 3
+        Phone.LOOKUP_KEY, // 4
+        Phone.PHOTO_ID, // 5
+        Phone.NUMBER, // 6
+        Phone.TYPE, // 7
+        Phone.LABEL, // 8
+        Phone.IS_SUPER_PRIMARY, // 9
+        Phone.PINNED, // 10
+        Phone.CONTACT_ID, // 11
+      };
+
+  private final ContentObserver contentObserver = new ForceLoadContentObserver();
+
+  StrequentContactsCursorLoader(Context context) {
+    super(
+        context,
+        buildUri(),
+        PHONE_PROJECTION,
+        null /* selection */,
+        null /* selectionArgs */,
+        null /* sortOrder */);
+    // TODO(calderwoodra): implement alternative display names
+  }
+
+  static void addToCursor(MatrixCursor dest, Cursor source) {
+    dest.newRow()
+        .add(PHONE_PROJECTION[PHONE_ID], source.getLong(PHONE_ID))
+        .add(PHONE_PROJECTION[PHONE_DISPLAY_NAME], source.getString(PHONE_DISPLAY_NAME))
+        .add(PHONE_PROJECTION[PHONE_STARRED], source.getInt(PHONE_STARRED))
+        .add(PHONE_PROJECTION[PHONE_PHOTO_URI], source.getString(PHONE_PHOTO_URI))
+        .add(PHONE_PROJECTION[PHONE_LOOKUP_KEY], source.getString(PHONE_LOOKUP_KEY))
+        .add(PHONE_PROJECTION[PHONE_NUMBER], source.getString(PHONE_NUMBER))
+        .add(PHONE_PROJECTION[PHONE_TYPE], source.getInt(PHONE_TYPE))
+        .add(PHONE_PROJECTION[PHONE_LABEL], source.getString(PHONE_LABEL))
+        .add(PHONE_PROJECTION[PHONE_IS_SUPER_PRIMARY], source.getInt(PHONE_IS_SUPER_PRIMARY))
+        .add(PHONE_PROJECTION[PHONE_PINNED], source.getInt(PHONE_PINNED))
+        .add(PHONE_PROJECTION[PHONE_CONTACT_ID], source.getLong(PHONE_CONTACT_ID));
+  }
+
+  private static Uri buildUri() {
+    return Contacts.CONTENT_STREQUENT_URI
+        .buildUpon()
+        .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true")
+        .build();
+  }
+
+  @Override
+  public Cursor loadInBackground() {
+    return SpeedDialCursor.newInstance(super.loadInBackground());
+  }
+
+  ContentObserver getContentObserver() {
+    return contentObserver;
+  }
+}
diff --git a/java/com/android/dialer/speeddial/SuggestionViewHolder.java b/java/com/android/dialer/speeddial/SuggestionViewHolder.java
new file mode 100644
index 0000000..70df307
--- /dev/null
+++ b/java/com/android/dialer/speeddial/SuggestionViewHolder.java
@@ -0,0 +1,117 @@
+/*
+ * 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.speeddial;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.support.v7.widget.RecyclerView;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.QuickContactBadge;
+import android.widget.TextView;
+import com.android.dialer.contactphoto.ContactPhotoManager;
+import com.android.dialer.lettertile.LetterTileDrawable;
+import com.android.dialer.location.GeoUtil;
+
+/** ViewHolder for displaying suggested contacts in {@link SpeedDialFragment}. */
+public class SuggestionViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
+
+  private final SuggestedContactsListener listener;
+
+  private final QuickContactBadge photoView;
+  private final TextView nameOrNumberView;
+  private final TextView numberView;
+
+  private String number;
+
+  SuggestionViewHolder(View view, SuggestedContactsListener listener) {
+    super(view);
+    photoView = view.findViewById(R.id.avatar);
+    nameOrNumberView = view.findViewById(R.id.name);
+    numberView = view.findViewById(R.id.number);
+    itemView.setOnClickListener(this);
+    view.findViewById(R.id.overflow).setOnClickListener(this);
+    this.listener = listener;
+  }
+
+  public void bind(Context context, Cursor cursor) {
+    number = cursor.getString(StrequentContactsCursorLoader.PHONE_NUMBER);
+    number = PhoneNumberUtils.formatNumber(number, GeoUtil.getCurrentCountryIso(context));
+
+    String name = cursor.getString(StrequentContactsCursorLoader.PHONE_DISPLAY_NAME);
+    String label = getLabel(context.getResources(), cursor);
+    String secondaryInfo =
+        TextUtils.isEmpty(label)
+            ? number
+            : context.getString(
+                com.android.contacts.common.R.string.call_subject_type_and_number, label, number);
+
+    nameOrNumberView.setText(name);
+    numberView.setText(secondaryInfo);
+
+    long contactId = cursor.getLong(StrequentContactsCursorLoader.PHONE_ID);
+    String lookupKey = cursor.getString(StrequentContactsCursorLoader.PHONE_LOOKUP_KEY);
+    Uri contactUri = Contacts.getLookupUri(contactId, lookupKey);
+
+    String photoUri = cursor.getString(StrequentContactsCursorLoader.PHONE_PHOTO_URI);
+    ContactPhotoManager.getInstance(context)
+        .loadDialerThumbnailOrPhoto(
+            photoView,
+            contactUri,
+            cursor.getLong(StrequentContactsCursorLoader.PHONE_PHOTO_ID),
+            photoUri == null ? null : Uri.parse(photoUri),
+            name,
+            LetterTileDrawable.TYPE_DEFAULT);
+  }
+
+  // TODO(calderwoodra): handle CNAP and cequint types.
+  // TODO(calderwoodra): unify this into a utility method with CallLogAdapter#getNumberType
+  private static String getLabel(Resources resources, Cursor cursor) {
+    int numberType = cursor.getInt(StrequentContactsCursorLoader.PHONE_TYPE);
+    String numberLabel = cursor.getString(StrequentContactsCursorLoader.PHONE_LABEL);
+
+    // Returns empty label instead of "custom" if the custom label is empty.
+    if (numberType == Phone.TYPE_CUSTOM && TextUtils.isEmpty(numberLabel)) {
+      return "";
+    }
+    return (String) Phone.getTypeLabel(resources, numberType, numberLabel);
+  }
+
+  @Override
+  public void onClick(View v) {
+    if (v.getId() == R.id.overflow) {
+      listener.onOverFlowMenuClicked(number);
+    } else {
+      listener.onRowClicked(number);
+    }
+  }
+
+  /** Listener/Callback for {@link SuggestionViewHolder} parents. */
+  public interface SuggestedContactsListener {
+
+    void onOverFlowMenuClicked(String number);
+
+    /** Called when a suggested contact is clicked. */
+    void onRowClicked(String number);
+  }
+}
diff --git a/java/com/android/dialer/speeddial/res/drawable/favorite_icon.xml b/java/com/android/dialer/speeddial/res/drawable/favorite_icon.xml
new file mode 100644
index 0000000..81b018f
--- /dev/null
+++ b/java/com/android/dialer/speeddial/res/drawable/favorite_icon.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+
+  <solid android:color="@color/dialer_theme_color"/>
+  <stroke android:color="@color/background_dialer_light" android:width="3dp"/>
+</shape>
\ No newline at end of file
diff --git a/java/com/android/dialer/speeddial/res/layout/favorite_item_layout.xml b/java/com/android/dialer/speeddial/res/layout/favorite_item_layout.xml
new file mode 100644
index 0000000..fb47665
--- /dev/null
+++ b/java/com/android/dialer/speeddial/res/layout/favorite_item_layout.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="8dp"
+    android:layout_marginBottom="12dp">
+
+  <FrameLayout
+      android:id="@+id/avatar_container"
+      android:layout_width="104dp"
+      android:layout_height="104dp"
+      android:layout_gravity="center_horizontal"
+      android:layout_marginBottom="8dp">
+
+    <com.android.dialer.speeddial.SquareImageView
+        android:id="@+id/avatar"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:clickable="false"/>
+
+    <FrameLayout
+        android:id="@+id/video_call_container"
+        android:layout_width="36dp"
+        android:layout_height="36dp"
+        android:layout_gravity="bottom|end">
+
+      <ImageView
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:src="@drawable/favorite_icon"/>
+
+      <ImageView
+          android:layout_width="24dp"
+          android:layout_height="24dp"
+          android:layout_gravity="center"
+          android:src="@drawable/quantum_ic_videocam_white_24"/>
+    </FrameLayout>
+  </FrameLayout>
+
+  <TextView
+      android:id="@+id/name"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_gravity="center_horizontal"
+      style="@style/PrimaryText"/>
+
+  <TextView
+      android:id="@+id/phone_type"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_gravity="center_horizontal"
+      style="@style/SecondaryText"/>
+</LinearLayout>
\ No newline at end of file
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 04e230e..d432f09 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
@@ -14,7 +14,11 @@
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License
  -->
-<FrameLayout
-  xmlns:android="http://schemas.android.com/apk/res/android"
-  android:layout_height="match_parent"
-  android:layout_width="match_parent"/>
+<android.support.v7.widget.RecyclerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/speed_dial_recycler_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingStart="16dp"
+    android:paddingEnd="16dp"
+    android:clipToPadding="false"/>
diff --git a/java/com/android/dialer/speeddial/res/layout/speed_dial_header_layout.xml b/java/com/android/dialer/speeddial/res/layout/speed_dial_header_layout.xml
new file mode 100644
index 0000000..0a84b41
--- /dev/null
+++ b/java/com/android/dialer/speeddial/res/layout/speed_dial_header_layout.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="@dimen/dialer_list_item_min_height">
+
+  <TextView
+      android:id="@+id/speed_dial_header_text"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_centerVertical="true"
+      android:textSize="16sp"
+      style="@style/SecondaryText"/>
+
+  <Button
+      android:id="@+id/speed_dial_add_button"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_alignParentEnd="true"
+      android:layout_centerVertical="true"
+      android:minHeight="@dimen/dialer_touch_target_min_size"
+      android:minWidth="@dimen/dialer_button_min_width"
+      android:text="@string/speed_dial_add_button_text"
+      android:textColor="@color/dialer_theme_color"
+      style="@style/Widget.AppCompat.Button.Borderless"/>
+</RelativeLayout>
\ No newline at end of file
diff --git a/java/com/android/dialer/speeddial/res/layout/suggestion_row_layout.xml b/java/com/android/dialer/speeddial/res/layout/suggestion_row_layout.xml
new file mode 100644
index 0000000..4281700
--- /dev/null
+++ b/java/com/android/dialer/speeddial/res/layout/suggestion_row_layout.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="72dp">
+
+  <QuickContactBadge
+      android:id="@+id/avatar"
+      android:layout_width="48dp"
+      android:layout_height="48dp"
+      android:layout_centerVertical="true"/>
+
+  <LinearLayout
+      android:orientation="vertical"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_marginStart="56dp"
+      android:layout_centerVertical="true"
+      android:layout_alignParentStart="true"
+      android:layout_toStartOf="@+id/overflow">
+
+    <TextView
+        android:id="@+id/name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        style="@style/PrimaryText"/>
+
+    <TextView
+        android:id="@+id/number"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        style="@style/SecondaryText"/>
+  </LinearLayout>
+
+  <ImageView
+      android:id="@+id/overflow"
+      android:layout_width="48dp"
+      android:layout_height="48dp"
+      android:layout_centerVertical="true"
+      android:layout_alignParentEnd="true"
+      android:scaleType="center"
+      android:tint="@color/secondary_text_color"
+      android:src="@drawable/quantum_ic_more_vert_white_24"
+      android:background="?android:selectableItemBackgroundBorderless"/>
+</RelativeLayout>
\ No newline at end of file
diff --git a/java/com/android/dialer/speeddial/res/values/strings.xml b/java/com/android/dialer/speeddial/res/values/strings.xml
index 5929df8..f814ed6 100644
--- a/java/com/android/dialer/speeddial/res/values/strings.xml
+++ b/java/com/android/dialer/speeddial/res/values/strings.xml
@@ -15,4 +15,12 @@
   ~ limitations under the License
   -->
 <resources>
+  <!-- header for a list of contacts that are the users favorites. -->
+  <string name="favorites_header">Favorites</string>
+
+  <!-- header for a list of contacts that are suggestions for the user to place calls to -->
+  <string name="suggestions_header">Suggestions</string>
+
+  <!-- text for a button that prompts the user to add a contact to their favorites -->
+  <string name="speed_dial_add_button_text">Add</string>
 </resources>
\ No newline at end of file
diff --git a/java/com/android/dialer/theme/res/values/dimens.xml b/java/com/android/dialer/theme/res/values/dimens.xml
index 972cb53..2b5243e 100644
--- a/java/com/android/dialer/theme/res/values/dimens.xml
+++ b/java/com/android/dialer/theme/res/values/dimens.xml
@@ -41,4 +41,13 @@
 
   <item name="alpha_enabled" format="float" type="dimen">1.0</item>
   <item name="alpha_hiden" format="float" type="dimen">0.54</item>
+
+  <!-- Minimum height required for all row elements in Dialer lists. -->
+  <dimen name="dialer_list_item_min_height">56dp</dimen>
+
+  <!-- Minimum a11y height and width required for all touch targets. -->
+  <dimen name="dialer_touch_target_min_size">48dp</dimen>
+
+  <!-- Minimum width for material compliant buttons. -->
+  <dimen name="dialer_button_min_width">72dp</dimen>
 </resources>