Merge "Drag favorite to remove"
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 b47806e..5cb457e 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,7 +19,9 @@
     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:layout_height="match_parent"
+    android:clipChildren="false"
+    android:clipToPadding="false">
 
   <!-- MainToolbar -->
   <include
@@ -33,13 +35,17 @@
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:layout_below="@+id/toolbar"
-      android:layout_above="@+id/bottom_nav_bar">
+      android:layout_above="@+id/bottom_nav_bar"
+      android:clipChildren="false"
+      android:clipToPadding="false">
 
     <!-- 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:layout_height="match_parent"
+        android:clipChildren="false"
+        android:clipToPadding="false"/>
 
     <FrameLayout
         android:id="@+id/search_fragment_container"
diff --git a/java/com/android/dialer/speeddial/FavoritesViewHolder.java b/java/com/android/dialer/speeddial/FavoritesViewHolder.java
index f06672d..600c8c7 100644
--- a/java/com/android/dialer/speeddial/FavoritesViewHolder.java
+++ b/java/com/android/dialer/speeddial/FavoritesViewHolder.java
@@ -46,6 +46,8 @@
   private final TextView phoneType;
   private final FrameLayout videoCallIcon;
 
+  private final FrameLayout avatarContainer;
+
   private SpeedDialUiItem speedDialUiItem;
 
   public FavoritesViewHolder(View view, ItemTouchHelper helper, FavoriteContactsListener listener) {
@@ -54,6 +56,7 @@
     nameView = view.findViewById(R.id.name);
     phoneType = view.findViewById(R.id.phone_type);
     videoCallIcon = view.findViewById(R.id.video_call_container);
+    avatarContainer = view.findViewById(R.id.avatar_container);
     view.setOnClickListener(this);
     view.setOnLongClickListener(this);
     view.setOnTouchListener(
@@ -117,6 +120,15 @@
     listener.onTouchFinished(closeContextMenu);
   }
 
+  FrameLayout getAvatarContainer() {
+    return avatarContainer;
+  }
+
+  void onSelectedChanged(boolean selected) {
+    nameView.setVisibility(selected ? View.GONE : View.VISIBLE);
+    phoneType.setVisibility(selected ? View.GONE : View.VISIBLE);
+  }
+
   /** Listener/callback for {@link FavoritesViewHolder} actions. */
   public interface FavoriteContactsListener {
 
@@ -131,5 +143,8 @@
 
     /** Called when the user is no longer touching the favorite contact. */
     void onTouchFinished(boolean closeContextMenu);
+
+    /** Called when the user drag the favorite to remove. */
+    void onRequestRemove(SpeedDialUiItem speedDialUiItem);
   }
 }
diff --git a/java/com/android/dialer/speeddial/RemoveViewHolder.java b/java/com/android/dialer/speeddial/RemoveViewHolder.java
new file mode 100644
index 0000000..998e1ae
--- /dev/null
+++ b/java/com/android/dialer/speeddial/RemoveViewHolder.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dialer.speeddial;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+/** ViewHolder for headers in {@link SpeedDialFragment}. */
+public class RemoveViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
+
+  private final View removeViewContent;
+
+  RemoveViewHolder(View view) {
+    super(view);
+    removeViewContent = view;
+  }
+
+  void show() {
+    removeViewContent.setVisibility(View.VISIBLE);
+    removeViewContent.setAlpha(0);
+    removeViewContent.animate().alpha(1).start();
+  }
+
+  void hide() {
+    removeViewContent.setVisibility(View.INVISIBLE);
+    removeViewContent.setAlpha(1);
+    removeViewContent.animate().alpha(0).start();
+  }
+
+  @Override
+  public void onClick(View v) {
+    // Not clickable
+  }
+}
diff --git a/java/com/android/dialer/speeddial/SpeedDialAdapter.java b/java/com/android/dialer/speeddial/SpeedDialAdapter.java
index a382b1a..ff52a07 100644
--- a/java/com/android/dialer/speeddial/SpeedDialAdapter.java
+++ b/java/com/android/dialer/speeddial/SpeedDialAdapter.java
@@ -21,6 +21,7 @@
 import android.os.Build.VERSION_CODES;
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.v7.widget.GridLayoutManager.SpanSizeLookup;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.ViewHolder;
@@ -28,6 +29,8 @@
 import android.util.ArrayMap;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
+import android.view.animation.AnticipateInterpolator;
+import android.widget.FrameLayout;
 import com.android.dialer.common.Assert;
 import com.android.dialer.speeddial.FavoritesViewHolder.FavoriteContactsListener;
 import com.android.dialer.speeddial.HeaderViewHolder.SpeedDialHeaderListener;
@@ -58,13 +61,20 @@
 public final class SpeedDialAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
     implements ItemTouchHelperAdapter {
 
+  private static final int NON_CONTACT_ITEM_NUMBER_BEFORE_FAVORITES = 2;
+  private static final int NON_CONTACT_ITEM_NUMBER_BEFORE_SUGGESTION = 3;
+
+  private static final float IN_REMOVE_VIEW_SCALE = 0.5f;
+  private static final float IN_REMOVE_VIEW_ALPHA = 0.5f;
+
   @Retention(RetentionPolicy.SOURCE)
   @IntDef({RowType.STARRED_HEADER, RowType.SUGGESTION_HEADER, RowType.STARRED, RowType.SUGGESTION})
   @interface RowType {
-    int STARRED_HEADER = 0;
-    int SUGGESTION_HEADER = 1;
-    int STARRED = 2;
-    int SUGGESTION = 3;
+    int REMOVE_VIEW = 0;
+    int STARRED_HEADER = 1;
+    int SUGGESTION_HEADER = 2;
+    int STARRED = 3;
+    int SUGGESTION = 4;
   }
 
   private final Context context;
@@ -78,6 +88,9 @@
   // Needed for FavoriteViewHolder
   private ItemTouchHelper itemTouchHelper;
 
+  private RemoveViewHolder removeViewHolder;
+  private FavoritesViewHolder draggingFavoritesViewHolder;
+
   public SpeedDialAdapter(
       Context context,
       FavoriteContactsListener favoritesListener,
@@ -111,6 +124,11 @@
       case RowType.SUGGESTION_HEADER:
         return new HeaderViewHolder(
             inflater.inflate(R.layout.speed_dial_header_layout, parent, false), headerListener);
+      case RowType.REMOVE_VIEW:
+        removeViewHolder =
+            new RemoveViewHolder(
+                inflater.inflate(R.layout.favorite_remove_view_layout, parent, false));
+        return removeViewHolder;
       default:
         throw Assert.createIllegalStateFailException("Invalid viewType: " + viewType);
     }
@@ -128,10 +146,17 @@
         ((HeaderViewHolder) holder).showAddButton(false);
         return;
       case RowType.STARRED:
-        ((FavoritesViewHolder) holder).bind(context, speedDialUiItems.get(position - 1));
+        ((FavoritesViewHolder) holder).bind(context, speedDialUiItems.get(position - 2));
+        // Removed item might come back
+        FrameLayout avatarContainer = ((FavoritesViewHolder) holder).getAvatarContainer();
+        avatarContainer.setScaleX(1);
+        avatarContainer.setScaleY(1);
+        avatarContainer.setAlpha(1);
         break;
       case RowType.SUGGESTION:
-        ((SuggestionViewHolder) holder).bind(context, speedDialUiItems.get(position - 2));
+        ((SuggestionViewHolder) holder).bind(context, speedDialUiItems.get(position - 3));
+        break;
+      case RowType.REMOVE_VIEW:
         break;
       default:
         throw Assert.createIllegalStateFailException("Invalid view holder: " + holder);
@@ -153,20 +178,25 @@
           }
           return Boolean.compare(o2.isStarred(), o1.isStarred());
         });
+    updatePositionToRowTypeMap();
+  }
+
+  private void updatePositionToRowTypeMap() {
     positionToRowTypeMap.clear();
     if (speedDialUiItems.isEmpty()) {
       return;
     }
 
+    positionToRowTypeMap.put(0, RowType.REMOVE_VIEW);
     // Show the add favorites even if there are no favorite contacts
-    positionToRowTypeMap.put(0, RowType.STARRED_HEADER);
-    int positionOfSuggestionHeader = 1;
+    positionToRowTypeMap.put(1, RowType.STARRED_HEADER);
+    int positionOfSuggestionHeader = NON_CONTACT_ITEM_NUMBER_BEFORE_FAVORITES;
     for (int i = 0; i < speedDialUiItems.size(); i++) {
       if (speedDialUiItems.get(i).isStarred()) {
-        positionToRowTypeMap.put(i + 1, RowType.STARRED); // +1 for the header
+        positionToRowTypeMap.put(i + NON_CONTACT_ITEM_NUMBER_BEFORE_FAVORITES, RowType.STARRED);
         positionOfSuggestionHeader++;
       } else {
-        positionToRowTypeMap.put(i + 2, RowType.SUGGESTION); // +2 for both headers
+        positionToRowTypeMap.put(i + NON_CONTACT_ITEM_NUMBER_BEFORE_SUGGESTION, RowType.SUGGESTION);
       }
     }
     if (!speedDialUiItems.get(speedDialUiItems.size() - 1).isStarred()) {
@@ -189,6 +219,7 @@
           case RowType.SUGGESTION:
           case RowType.STARRED_HEADER:
           case RowType.SUGGESTION_HEADER:
+          case RowType.REMOVE_VIEW:
             return 3; // span the whole screen
           case RowType.STARRED:
             return 1; // span 1/3 of the screen
@@ -202,15 +233,88 @@
 
   @Override
   public void onItemMove(int fromPosition, int toPosition) {
+    if (toPosition == 0) {
+      // drop to removeView
+      return;
+    }
     // fromPosition/toPosition correspond to adapter position, which is off by 1 from the list
     // position b/c of the favorites header. So subtract 1 here.
-    speedDialUiItems.add(toPosition - 1, speedDialUiItems.remove(fromPosition - 1));
+    speedDialUiItems.add(toPosition - 2, speedDialUiItems.remove(fromPosition - 2));
     notifyItemMoved(fromPosition, toPosition);
   }
 
   @Override
   public boolean canDropOver(ViewHolder target) {
-    return target instanceof FavoritesViewHolder;
+    return target instanceof FavoritesViewHolder || target instanceof RemoveViewHolder;
+  }
+
+  @Override
+  public void onSelectedChanged(@Nullable ViewHolder viewHolder, int actionState) {
+    switch (actionState) {
+      case ItemTouchHelper.ACTION_STATE_DRAG:
+        if (viewHolder != null) {
+          draggingFavoritesViewHolder = (FavoritesViewHolder) viewHolder;
+          draggingFavoritesViewHolder.onSelectedChanged(true);
+          removeViewHolder.show();
+        }
+        break;
+      case ItemTouchHelper.ACTION_STATE_IDLE:
+        // viewHolder is null in this case
+        if (draggingFavoritesViewHolder != null) {
+          draggingFavoritesViewHolder.onSelectedChanged(false);
+          draggingFavoritesViewHolder = null;
+          removeViewHolder.hide();
+        }
+        break;
+      default:
+        break;
+    }
+  }
+
+  @Override
+  public void enterRemoveView() {
+    if (draggingFavoritesViewHolder != null) {
+      draggingFavoritesViewHolder
+          .getAvatarContainer()
+          .animate()
+          .scaleX(IN_REMOVE_VIEW_SCALE)
+          .scaleY(IN_REMOVE_VIEW_SCALE)
+          .alpha(IN_REMOVE_VIEW_ALPHA)
+          .start();
+    }
+  }
+
+  @Override
+  public void leaveRemoveView() {
+    if (draggingFavoritesViewHolder != null) {
+      draggingFavoritesViewHolder
+          .getAvatarContainer()
+          .animate()
+          .scaleX(1)
+          .scaleY(1)
+          .alpha(1)
+          .start();
+    }
+  }
+
+  @Override
+  public void dropOnRemoveView(ViewHolder fromViewHolder) {
+    if (!(fromViewHolder instanceof FavoritesViewHolder)) {
+      return;
+    }
+    int fromPosition = fromViewHolder.getAdapterPosition();
+
+    SpeedDialUiItem removedItem = speedDialUiItems.remove(fromPosition - 2);
+    favoritesListener.onRequestRemove(removedItem);
+    ((FavoritesViewHolder) fromViewHolder)
+        .getAvatarContainer()
+        .animate()
+        .scaleX(0)
+        .scaleY(0)
+        .alpha(0)
+        .setInterpolator(new AnticipateInterpolator())
+        .start();
+    updatePositionToRowTypeMap();
   }
 
   public void setItemTouchHelper(ItemTouchHelper itemTouchHelper) {
diff --git a/java/com/android/dialer/speeddial/SpeedDialFragment.java b/java/com/android/dialer/speeddial/SpeedDialFragment.java
index c48eb8d..aabf28b 100644
--- a/java/com/android/dialer/speeddial/SpeedDialFragment.java
+++ b/java/com/android/dialer/speeddial/SpeedDialFragment.java
@@ -151,9 +151,9 @@
         new SpeedDialFavoritesListener(
             getActivity(),
             getChildFragmentManager(),
-            new SpeedDialContextMenuItemListener(
-                getActivity(), new UpdateSpeedDialAdapterListener(), speedDialLoaderListener),
-            layoutManager);
+            layoutManager,
+            new UpdateSpeedDialAdapterListener(),
+            speedDialLoaderListener);
     adapter =
         new SpeedDialAdapter(getContext(), favoritesListener, suggestedListener, headerListener);
     layoutManager.setSpanSizeLookup(adapter.getSpanSizeLookup());
@@ -339,20 +339,26 @@
 
     private final FragmentActivity activity;
     private final FragmentManager childFragmentManager;
-    private final ContextMenuItemListener contextMenuListener;
     private final SpeedDialLayoutManager layoutManager;
+    private final UpdateSpeedDialAdapterListener updateAdapterListener;
+    private final SupportUiListener<ImmutableList<SpeedDialUiItem>> speedDialLoaderListener;
+
+    private final SpeedDialContextMenuItemListener speedDialContextMenuItemListener =
+        new SpeedDialContextMenuItemListener();
 
     private ContextMenu contextMenu;
 
     SpeedDialFavoritesListener(
         FragmentActivity activity,
         FragmentManager childFragmentManager,
-        ContextMenuItemListener contextMenuListener,
-        SpeedDialLayoutManager layoutManager) {
+        SpeedDialLayoutManager layoutManager,
+        UpdateSpeedDialAdapterListener updateAdapterListener,
+        SupportUiListener<ImmutableList<SpeedDialUiItem>> speedDialLoaderListener) {
       this.activity = activity;
       this.childFragmentManager = childFragmentManager;
-      this.contextMenuListener = contextMenuListener;
       this.layoutManager = layoutManager;
+      this.updateAdapterListener = updateAdapterListener;
+      this.speedDialLoaderListener = speedDialLoaderListener;
     }
 
     @Override
@@ -384,7 +390,8 @@
     @Override
     public void showContextMenu(View view, SpeedDialUiItem speedDialUiItem) {
       layoutManager.setScrollEnabled(false);
-      contextMenu = ContextMenu.show(activity, view, contextMenuListener, speedDialUiItem);
+      contextMenu =
+          ContextMenu.show(activity, view, speedDialContextMenuItemListener, speedDialUiItem);
     }
 
     @Override
@@ -397,12 +404,66 @@
       }
     }
 
-    public void hideMenu() {
+    @Override
+    public void onRequestRemove(SpeedDialUiItem speedDialUiItem) {
+      speedDialContextMenuItemListener.removeFavoriteContact(speedDialUiItem);
+    }
+
+    void hideMenu() {
       if (contextMenu != null) {
         contextMenu.hide();
         contextMenu = null;
       }
     }
+
+    public SpeedDialContextMenuItemListener getSpeedDialContextMenuItemListener() {
+      return speedDialContextMenuItemListener;
+    }
+
+    class SpeedDialContextMenuItemListener implements ContextMenuItemListener {
+
+      @Override
+      public void placeCall(Channel channel) {
+        if (channel.technology() == Channel.DUO) {
+          Logger.get(activity)
+              .logImpression(
+                  DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FOR_FAVORITE_CONTACT);
+        }
+        PreCall.start(
+            activity,
+            new CallIntentBuilder(channel.number(), CallInitiationType.Type.SPEED_DIAL)
+                .setAllowAssistedDial(true)
+                .setIsVideoCall(channel.isVideoTechnology())
+                .setIsDuoCall(channel.technology() == Channel.DUO));
+      }
+
+      @Override
+      public void openSmsConversation(String number) {
+        activity.startActivity(IntentUtil.getSendSmsIntent(number));
+      }
+
+      @Override
+      public void removeFavoriteContact(SpeedDialUiItem speedDialUiItem) {
+        speedDialLoaderListener.listen(
+            activity,
+            UiItemLoaderComponent.get(activity)
+                .speedDialUiItemMutator()
+                .removeSpeedDialUiItem(speedDialUiItem),
+            updateAdapterListener::updateAdapter,
+            throwable -> {
+              throw new RuntimeException(throwable);
+            });
+      }
+
+      @Override
+      public void openContactInfo(SpeedDialUiItem speedDialUiItem) {
+        activity.startActivity(
+            new Intent(
+                Intent.ACTION_VIEW,
+                Uri.withAppendedPath(
+                    Contacts.CONTENT_URI, String.valueOf(speedDialUiItem.contactId()))));
+      }
+    }
   }
 
   private final class SpeedDialSuggestedListener implements SuggestedContactsListener {
@@ -530,63 +591,6 @@
     }
   }
 
-  private static final class SpeedDialContextMenuItemListener implements ContextMenuItemListener {
-
-    private final FragmentActivity activity;
-    private final SupportUiListener<ImmutableList<SpeedDialUiItem>> speedDialLoaderListener;
-    private final UpdateSpeedDialAdapterListener updateAdapterListener;
-
-    SpeedDialContextMenuItemListener(
-        FragmentActivity activity,
-        UpdateSpeedDialAdapterListener updateAdapterListener,
-        SupportUiListener<ImmutableList<SpeedDialUiItem>> speedDialLoaderListener) {
-      this.activity = activity;
-      this.updateAdapterListener = updateAdapterListener;
-      this.speedDialLoaderListener = speedDialLoaderListener;
-    }
-
-    @Override
-    public void placeCall(Channel channel) {
-      if (channel.technology() == Channel.DUO) {
-        Logger.get(activity)
-            .logImpression(DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FOR_FAVORITE_CONTACT);
-      }
-      PreCall.start(
-          activity,
-          new CallIntentBuilder(channel.number(), CallInitiationType.Type.SPEED_DIAL)
-              .setAllowAssistedDial(true)
-              .setIsVideoCall(channel.isVideoTechnology())
-              .setIsDuoCall(channel.technology() == Channel.DUO));
-    }
-
-    @Override
-    public void openSmsConversation(String number) {
-      activity.startActivity(IntentUtil.getSendSmsIntent(number));
-    }
-
-    @Override
-    public void removeFavoriteContact(SpeedDialUiItem speedDialUiItem) {
-      speedDialLoaderListener.listen(
-          activity,
-          UiItemLoaderComponent.get(activity)
-              .speedDialUiItemMutator()
-              .removeSpeedDialUiItem(speedDialUiItem),
-          updateAdapterListener::updateAdapter,
-          throwable -> {
-            throw new RuntimeException(throwable);
-          });
-    }
-
-    @Override
-    public void openContactInfo(SpeedDialUiItem speedDialUiItem) {
-      activity.startActivity(
-          new Intent(
-              Intent.ACTION_VIEW,
-              Uri.withAppendedPath(
-                  Contacts.CONTENT_URI, String.valueOf(speedDialUiItem.contactId()))));
-    }
-  }
-
   private static final class SpeedDialContactPermissionEmptyViewListener
       implements OnEmptyViewActionButtonClickedListener {
 
@@ -628,7 +632,7 @@
   }
 
   /** Listener for when a SpeedDialUiItem is updated. */
-  private class UpdateSpeedDialAdapterListener {
+  class UpdateSpeedDialAdapterListener {
 
     void updateAdapter(ImmutableList<SpeedDialUiItem> speedDialUiItems) {
       onSpeedDialUiItemListLoaded(speedDialUiItems);
diff --git a/java/com/android/dialer/speeddial/draghelper/SpeedDialItemTouchHelperCallback.java b/java/com/android/dialer/speeddial/draghelper/SpeedDialItemTouchHelperCallback.java
index d1d9f47..fc963a1 100644
--- a/java/com/android/dialer/speeddial/draghelper/SpeedDialItemTouchHelperCallback.java
+++ b/java/com/android/dialer/speeddial/draghelper/SpeedDialItemTouchHelperCallback.java
@@ -16,7 +16,9 @@
 
 package com.android.dialer.speeddial.draghelper;
 
+import android.graphics.Canvas;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.support.v7.widget.helper.ItemTouchHelper;
@@ -26,6 +28,12 @@
 
   private final ItemTouchHelperAdapter adapter;
 
+  // When dragged item is in removeView, onMove() and onChildDraw() are called in turn. This
+  // behavior changes when dragged item entering/leaving removeView. The boolean field
+  // movedOverRemoveView is for onMove() and onChildDraw() to flip.
+  private boolean movedOverRemoveView;
+  private boolean inRemoveView;
+
   public SpeedDialItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
     this.adapter = adapter;
   }
@@ -64,11 +72,57 @@
       @NonNull RecyclerView recyclerView,
       @NonNull ViewHolder viewHolder,
       @NonNull ViewHolder target) {
+    if (target.getItemViewType() == 0) { // 0 for RowType.REMOVE_VIEW
+      movedOverRemoveView = true;
+      if (!inRemoveView) {
+        // onMove() first called
+        adapter.enterRemoveView();
+        inRemoveView = true;
+      }
+      return false;
+    } else if (inRemoveView) {
+      // Move out of removeView fast
+      inRemoveView = false;
+      movedOverRemoveView = false;
+      adapter.leaveRemoveView();
+    }
     adapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
     return true;
   }
 
   @Override
+  public void onChildDraw(
+      @NonNull Canvas canvas,
+      @NonNull RecyclerView recyclerView,
+      @NonNull ViewHolder viewHolder,
+      float dx,
+      float dy,
+      int i,
+      boolean isCurrentlyActive) {
+    if (inRemoveView) {
+      if (!isCurrentlyActive) {
+        // View animating back to its original state, which means drop in this case
+        inRemoveView = false;
+        adapter.dropOnRemoveView(viewHolder);
+      }
+      if (!movedOverRemoveView) {
+        // when the view is over a droppable target, onMove() will be called before onChildDraw()
+        // thus if onMove() is not called, it is not over a droppable target.
+        inRemoveView = false;
+        adapter.leaveRemoveView();
+      }
+    }
+    movedOverRemoveView = false;
+    super.onChildDraw(canvas, recyclerView, viewHolder, dx, dy, i, isCurrentlyActive);
+  }
+
+  @Override
+  public void onSelectedChanged(@Nullable ViewHolder viewHolder, int actionState) {
+    super.onSelectedChanged(viewHolder, actionState);
+    adapter.onSelectedChanged(viewHolder, actionState);
+  }
+
+  @Override
   public void onSwiped(@NonNull ViewHolder viewHolder, int direction) {
     // No-op since we don't support swiping
   }
@@ -79,5 +133,13 @@
     void onItemMove(int fromPosition, int toPosition);
 
     boolean canDropOver(ViewHolder target);
+
+    void onSelectedChanged(@Nullable ViewHolder viewHolder, int actionState);
+
+    void enterRemoveView();
+
+    void leaveRemoveView();
+
+    void dropOnRemoveView(ViewHolder fromViewHolder);
   }
 }
diff --git a/java/com/android/dialer/speeddial/res/layout/favorite_remove_view_layout.xml b/java/com/android/dialer/speeddial/res/layout/favorite_remove_view_layout.xml
new file mode 100644
index 0000000..825658c
--- /dev/null
+++ b/java/com/android/dialer/speeddial/res/layout/favorite_remove_view_layout.xml
@@ -0,0 +1,49 @@
+<?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
+ -->
+
+<!-- Sets android:importantForAccessibility="no" to avoid being announced when navigating with
+         talkback enabled. It will still be announced when user drag or drop contact onto it.
+         This is required since drag and drop event is only sent to views are visible when drag
+         starts. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/favorite_remove_view"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/expanded_search_bar_height"
+    android:background="?android:attr/colorPrimary"
+    android:gravity="center"
+    android:orientation="horizontal"
+    android:visibility="invisible"
+    android:contentDescription="@string/speed_dial_remove_favorites"
+    android:importantForAccessibility="no">
+
+  <ImageView
+      android:id="@+id/remove_view_icon"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_marginTop="8dp"
+      android:layout_marginBottom="8dp"
+      android:src="@drawable/quantum_ic_close_vd_theme_24"
+      android:tint="?colorIconOnUnthemedDarkBackground"/>
+
+  <TextView
+      android:id="@+id/remove_view_text"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:text="@string/speed_dial_remove_favorites"
+      android:textColor="?colorTextOnUnthemedDarkBackground"
+      android:textSize="16sp"/>
+</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 e289bb7..472f9e9 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,14 +17,17 @@
 <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:layout_height="match_parent"
+    android:clipChildren="false"
+    android:clipToPadding="false">
 
   <android.support.v7.widget.RecyclerView
       android:id="@+id/speed_dial_recycler_view"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
+      android:layout_marginTop="@dimen/expanded_search_bar_height_minus"
       android:clipToPadding="false"
-      android:background="?android:attr/colorBackground"
+      android:background="@android:color/transparent"
       android:paddingBottom="@dimen/floating_action_button_list_bottom_padding"/>
 
   <com.android.dialer.widget.EmptyContentView
diff --git a/java/com/android/dialer/speeddial/res/values/dimens.xml b/java/com/android/dialer/speeddial/res/values/dimens.xml
index a4cf16c..624c60d 100644
--- a/java/com/android/dialer/speeddial/res/values/dimens.xml
+++ b/java/com/android/dialer/speeddial/res/values/dimens.xml
@@ -32,4 +32,6 @@
   <dimen name="speed_dial_context_menu_x_offset">-24dp</dimen>
   <dimen name="speed_dial_context_menu_corner_radius">16dp</dimen>
   <dimen name="disambig_dialog_width">280dp</dimen>
+  <dimen name="expanded_search_bar_height">64dp</dimen>
+  <dimen name="expanded_search_bar_height_minus">-64dp</dimen>
 </resources>
\ 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 7f8fed5..397ec8f 100644
--- a/java/com/android/dialer/speeddial/res/values/strings.xml
+++ b/java/com/android/dialer/speeddial/res/values/strings.xml
@@ -71,4 +71,7 @@
 
   <!-- [CHAR LIMIT=NONE] Shown as an action when the all contacts list is empty -->
   <string name="speed_dial_no_contacts_action_text">Add favorite</string>
+
+  <!-- Text shown on a dragging target that removes the contact from favorites -->
+  <string name="speed_dial_remove_favorites">Remove from favorites</string>
 </resources>
\ No newline at end of file