Migrated context menu to be a PopupMenu instead.

This gives us huge amounts of functionality with very little technical cost but
some trade off in UX.

Bug: 77761183,78492250
Test: existing
PiperOrigin-RevId: 195133774
Change-Id: I57e48b5defc4ae1c7bfbed13e3fbc16ebd607944
diff --git a/java/com/android/dialer/speeddial/ContextMenu.java b/java/com/android/dialer/speeddial/ContextMenu.java
index 09505ab..e0a4551 100644
--- a/java/com/android/dialer/speeddial/ContextMenu.java
+++ b/java/com/android/dialer/speeddial/ContextMenu.java
@@ -17,101 +17,107 @@
 package com.android.dialer.speeddial;
 
 import android.content.Context;
-import android.support.annotation.Nullable;
+import android.support.annotation.NonNull;
 import android.support.annotation.VisibleForTesting;
-import android.util.AttributeSet;
+import android.support.v7.widget.PopupMenu;
+import android.support.v7.widget.PopupMenu.OnMenuItemClickListener;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.MenuItem;
 import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.TextView;
 import com.android.dialer.common.Assert;
 import com.android.dialer.speeddial.database.SpeedDialEntry.Channel;
 import com.android.dialer.speeddial.loader.SpeedDialUiItem;
 
-/** Floating menu which presents contact options available to the contact. */
-public class ContextMenu extends LinearLayout {
+/** {@link PopupMenu} which presents contact options for starred contacts. */
+public class ContextMenu extends PopupMenu implements OnMenuItemClickListener {
 
-  private ContextMenuItemListener listener;
+  private final ContextMenuItemListener listener;
 
-  private TextView voiceView;
-  private TextView videoView;
-  private TextView smsView;
+  private final SpeedDialUiItem speedDialUiItem;
+  private final Channel voiceChannel;
+  private final Channel videoChannel;
 
-  private SpeedDialUiItem speedDialUiItem;
-  private Channel voiceChannel;
-  private Channel videoChannel;
+  private boolean visible;
 
-  public ContextMenu(Context context, @Nullable AttributeSet attrs) {
-    super(context, attrs);
+  /**
+   * Creates a new context menu and displays it.
+   *
+   * @see #show()
+   */
+  public static ContextMenu show(
+      Context context,
+      View anchor,
+      ContextMenuItemListener contextMenuListener,
+      SpeedDialUiItem speedDialUiItem) {
+    ContextMenu menu = new ContextMenu(context, anchor, contextMenuListener, speedDialUiItem);
+    menu.show();
+    menu.visible = true;
+    return menu;
   }
 
-  @Override
-  protected void onFinishInflate() {
-    super.onFinishInflate();
-
-    videoView = findViewById(R.id.video_call_container);
-    videoView.setOnClickListener(v -> placeVideoCall());
-
-    smsView = findViewById(R.id.send_message_container);
-    smsView.setOnClickListener(v -> listener.openSmsConversation(voiceChannel.number()));
-
-    voiceView = findViewById(R.id.voice_call_container);
-    voiceView.setOnClickListener(v -> placeVoiceCall());
-
-    findViewById(R.id.remove_container)
-        .setOnClickListener(v -> listener.removeFavoriteContact(speedDialUiItem));
-    findViewById(R.id.contact_info_container)
-        .setOnClickListener(v -> listener.openContactInfo(speedDialUiItem));
+  /**
+   * Hides the context menu.
+   *
+   * @see #dismiss()
+   */
+  public void hide() {
+    dismiss();
+    visible = false;
   }
 
-  /** Shows the menu and updates the menu's position w.r.t. the view it's related to. */
-  public void showMenu(
-      View parentLayout,
-      View childLayout,
-      SpeedDialUiItem speedDialUiItem,
-      ContextMenuItemListener listener) {
-    this.speedDialUiItem = speedDialUiItem;
+  private ContextMenu(
+      @NonNull Context context,
+      @NonNull View anchor,
+      ContextMenuItemListener listener,
+      SpeedDialUiItem speedDialUiItem) {
+    super(context, anchor, Gravity.CENTER);
     this.listener = listener;
-
-    int[] childLocation = new int[2];
-    int[] parentLocation = new int[2];
-    childLayout.getLocationOnScreen(childLocation);
-    parentLayout.getLocationOnScreen(parentLocation);
-
-    setX((float) (childLocation[0] + .5 * childLayout.getWidth() - .5 * getWidth()));
-    setY(childLocation[1] - parentLocation[1] + childLayout.getHeight());
-
+    this.speedDialUiItem = speedDialUiItem;
     voiceChannel = speedDialUiItem.getDefaultVoiceChannel();
     videoChannel = speedDialUiItem.getDefaultVideoChannel();
-    voiceView.setVisibility(videoChannel == null ? View.GONE : View.VISIBLE);
-    videoView.setVisibility(videoChannel == null ? View.GONE : View.VISIBLE);
-    smsView.setVisibility(voiceChannel == null ? View.GONE : View.VISIBLE);
 
-    // TODO(calderwoodra): a11y
-    // TODO(calderwoodra): animate this similar to the bubble menu
-    setVisibility(View.VISIBLE);
-  }
-
-  /** Returns true if the view was hidden. */
-  public void hideMenu() {
-    this.speedDialUiItem = null;
-    this.listener = null;
-    if (getVisibility() == View.VISIBLE) {
-      // TODO(calderwoodra): a11y
-      // TODO(calderwoodra): animate this similar to the bubble menu
-      setVisibility(View.INVISIBLE);
+    setOnMenuItemClickListener(this);
+    getMenuInflater().inflate(R.menu.starred_contact_context_menu, getMenu());
+    getMenu().findItem(R.id.voice_call_container).setVisible(voiceChannel != null);
+    getMenu().findItem(R.id.video_call_container).setVisible(videoChannel != null);
+    getMenu().findItem(R.id.send_message_container).setVisible(voiceChannel != null);
+    if (voiceChannel != null) {
+      String secondaryInfo =
+          TextUtils.isEmpty(voiceChannel.label())
+              ? voiceChannel.number()
+              : context.getString(
+                  R.string.call_subject_type_and_number,
+                  voiceChannel.label(),
+                  voiceChannel.number());
+      getMenu().findItem(R.id.starred_contact_context_menu_title).setTitle(secondaryInfo);
+      getMenu().findItem(R.id.starred_contact_context_menu_title).setVisible(true);
+    } else {
+      getMenu().findItem(R.id.starred_contact_context_menu_title).setVisible(false);
     }
   }
 
-  private void placeVoiceCall() {
-    listener.placeCall(Assert.isNotNull(voiceChannel));
+  @Override
+  public boolean onMenuItemClick(MenuItem menuItem) {
+    if (menuItem.getItemId() == R.id.voice_call_container) {
+      listener.placeCall(Assert.isNotNull(voiceChannel));
+    } else if (menuItem.getItemId() == R.id.video_call_container) {
+      listener.placeCall(Assert.isNotNull(videoChannel));
+    } else if (menuItem.getItemId() == R.id.send_message_container) {
+      listener.openSmsConversation(voiceChannel.number());
+    } else if (menuItem.getItemId() == R.id.remove_container) {
+      listener.removeFavoriteContact(speedDialUiItem);
+    } else if (menuItem.getItemId() == R.id.contact_info_container) {
+      listener.openContactInfo(speedDialUiItem);
+    } else {
+      throw Assert.createIllegalStateFailException("Menu option click not handled");
+    }
+    return true;
   }
 
-  private void placeVideoCall() {
-    listener.placeCall(Assert.isNotNull(videoChannel));
-  }
-
+  @VisibleForTesting(otherwise = VisibleForTesting.NONE)
   public boolean isVisible() {
-    return getVisibility() == View.VISIBLE;
+    return visible;
   }
 
   /** Listener to report user clicks on menu items. */
diff --git a/java/com/android/dialer/speeddial/SpeedDialFragment.java b/java/com/android/dialer/speeddial/SpeedDialFragment.java
index 702472c..f0ba186 100644
--- a/java/com/android/dialer/speeddial/SpeedDialFragment.java
+++ b/java/com/android/dialer/speeddial/SpeedDialFragment.java
@@ -32,7 +32,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.FrameLayout;
 import com.android.dialer.callintent.CallInitiationType;
 import com.android.dialer.callintent.CallIntentBuilder;
 import com.android.dialer.common.FragmentUtils;
@@ -82,11 +81,9 @@
   private final SpeedDialHeaderListener headerListener = new SpeedDialFragmentHeaderListener();
   private final SpeedDialSuggestedListener suggestedListener = new SpeedDialSuggestedListener();
 
-  private ContextMenu contextMenu;
-  private FrameLayout contextMenuBackground;
-
   private SpeedDialAdapter adapter;
   private SupportUiListener<ImmutableList<SpeedDialUiItem>> speedDialLoaderListener;
+  private SpeedDialFavoritesListener favoritesListener;
 
   /**
    * We update the UI every time the fragment is resumed. This boolean suppresses that functionality
@@ -109,25 +106,13 @@
         DialerExecutorComponent.get(getContext())
             .createUiListener(getChildFragmentManager(), "speed_dial_loader_listener");
 
-    // Setup favorite contact context menu
-    contextMenu = rootLayout.findViewById(R.id.favorite_contact_context_menu);
-    contextMenuBackground = rootLayout.findViewById(R.id.context_menu_background);
-    contextMenuBackground.setOnClickListener(
-        v -> {
-          contextMenu.hideMenu();
-          contextMenuBackground.setVisibility(View.GONE);
-        });
-
     // Setup our RecyclerView
     SpeedDialLayoutManager layoutManager =
         new SpeedDialLayoutManager(getContext(), 3 /* spanCount */);
-    FavoriteContactsListener favoritesListener =
+    favoritesListener =
         new SpeedDialFavoritesListener(
             getActivity(),
             getChildFragmentManager(),
-            rootLayout,
-            contextMenu,
-            contextMenuBackground,
             new SpeedDialContextMenuItemListener(
                 getActivity(),
                 new UpdateSpeedDialAdapterListener(),
@@ -200,8 +185,7 @@
   @Override
   public void onPause() {
     super.onPause();
-    contextMenu.hideMenu();
-    contextMenuBackground.setVisibility(View.GONE);
+    favoritesListener.hideMenu();
     Futures.addCallback(
         DialerExecutorComponent.get(getContext())
             .backgroundExecutor()
@@ -217,15 +201,6 @@
     suggestedListener.onPause();
   }
 
-  @Override
-  public void onHiddenChanged(boolean hidden) {
-    super.onHiddenChanged(hidden);
-    if (hidden) {
-      contextMenu.hideMenu();
-      contextMenuBackground.setVisibility(View.GONE);
-    }
-  }
-
   private class SpeedDialFragmentHeaderListener implements SpeedDialHeaderListener {
 
     @Override
@@ -239,25 +214,18 @@
 
     private final FragmentActivity activity;
     private final FragmentManager childFragmentManager;
-    private final View rootLayout;
-    private final ContextMenu contextMenu;
-    private final View contextMenuBackground;
     private final ContextMenuItemListener contextMenuListener;
     private final SpeedDialLayoutManager layoutManager;
 
+    private ContextMenu contextMenu;
+
     SpeedDialFavoritesListener(
         FragmentActivity activity,
         FragmentManager childFragmentManager,
-        View rootLayout,
-        ContextMenu contextMenu,
-        View contextMenuBackground,
         ContextMenuItemListener contextMenuListener,
         SpeedDialLayoutManager layoutManager) {
       this.activity = activity;
       this.childFragmentManager = childFragmentManager;
-      this.rootLayout = rootLayout;
-      this.contextMenu = contextMenu;
-      this.contextMenuBackground = contextMenuBackground;
       this.contextMenuListener = contextMenuListener;
       this.layoutManager = layoutManager;
     }
@@ -292,7 +260,7 @@
     @Override
     public void showContextMenu(View view, SpeedDialUiItem speedDialUiItem) {
       layoutManager.setScrollEnabled(false);
-      contextMenu.showMenu(rootLayout, view, speedDialUiItem, contextMenuListener);
+      contextMenu = ContextMenu.show(activity, view, contextMenuListener, speedDialUiItem);
     }
 
     @Override
@@ -300,14 +268,15 @@
       layoutManager.setScrollEnabled(true);
 
       if (closeContextMenu) {
-        contextMenu.hideMenu();
-      } else if (contextMenu.isVisible()) {
-        // If we're showing the context menu, show this background surface so that we can intercept
-        // touch events to close the menu
-        // Note: We call this in onTouchFinished because if we show the background before the user
-        // is done, they might try to drag the view and but won't be able to because this view would
-        // intercept all of the touch events.
-        contextMenuBackground.setVisibility(View.VISIBLE);
+        contextMenu.hide();
+        contextMenu = null;
+      }
+    }
+
+    public void hideMenu() {
+      if (contextMenu != null) {
+        contextMenu.hide();
+        contextMenu = null;
       }
     }
   }
diff --git a/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java b/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java
index 325af23..f730d12 100644
--- a/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java
+++ b/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java
@@ -222,16 +222,29 @@
    */
   @Nullable
   public Channel getDefaultVoiceChannel() {
-    if (defaultChannel() != null && !defaultChannel().isVideoTechnology()) {
-      return defaultChannel();
-    }
-
     if (channels().size() == 1) {
       // If there is only a single channel, it must be a voice channel as per our defined
       // assumptions (detailed in comments on method channels()).
       return channels().get(0);
     }
 
+    if (defaultChannel() == null) {
+      return null;
+    }
+
+    if (!defaultChannel().isVideoTechnology()) {
+      return defaultChannel();
+    }
+
+    // Default channel is a video channel, so find it's corresponding voice channel
+    Channel prevChannel = channels().get(0);
+    for (int i = 1; i < channels().size(); i++) {
+      Channel currentChannel = channels().get(i);
+      if (currentChannel.equals(defaultChannel())) {
+        return prevChannel;
+      }
+      prevChannel = currentChannel;
+    }
     return null;
   }
 
diff --git a/java/com/android/dialer/speeddial/res/layout/context_menu_layout.xml b/java/com/android/dialer/speeddial/res/layout/context_menu_layout.xml
deleted file mode 100644
index a59fa07..0000000
--- a/java/com/android/dialer/speeddial/res/layout/context_menu_layout.xml
+++ /dev/null
@@ -1,93 +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
- -->
-<com.android.dialer.speeddial.ContextMenu
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/contact_menu_container"
-    android:orientation="vertical"
-    android:layout_width="160dp"
-    android:layout_height="wrap_content"
-    android:clipChildren="true"
-    android:clipToPadding="false">
-
-  <FrameLayout
-      android:layout_width="12dp"
-      android:layout_height="12dp"
-      android:layout_marginTop="7dp"
-      android:layout_marginBottom="-6dp"
-      android:layout_gravity="center_horizontal"
-      android:background="@color/background_dialer_white"
-      android:rotation="45"
-      android:clipChildren="false"
-      android:clipToPadding="false"/>
-
-  <LinearLayout
-      android:id="@+id/context_menu_items_container"
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:orientation="vertical"
-      android:background="@drawable/context_menu_background">
-
-
-    <TextView
-        android:id="@+id/voice_call_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/contact_menu_voice_call"
-        android:drawableStart="@drawable/quantum_ic_phone_vd_theme_24"
-        style="@style/SpeedDialContextMenuItem"/>
-
-    <TextView
-        android:id="@+id/video_call_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/contact_menu_video_call"
-        android:drawableStart="@drawable/quantum_ic_videocam_vd_theme_24"
-        style="@style/SpeedDialContextMenuItem"/>
-
-    <TextView
-        android:id="@+id/send_message_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingBottom="4dp"
-        android:text="@string/contact_menu_message"
-        android:drawableStart="@drawable/quantum_ic_message_vd_theme_24"
-        style="@style/SpeedDialContextMenuItem"/>
-
-    <View
-        android:id="@+id/divider"
-        android:layout_width="match_parent"
-        android:layout_height="1dp"
-        android:background="@color/divider_line_color"/>
-
-    <TextView
-        android:id="@+id/remove_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingTop="4dp"
-        android:text="@string/contact_menu_remove"
-        android:drawableStart="@drawable/quantum_ic_close_vd_theme_24"
-        style="@style/SpeedDialContextMenuItem"/>
-
-    <TextView
-        android:id="@+id/contact_info_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:drawableStart="@drawable/context_menu_contact_icon"
-        android:text="@string/contact_menu_contact_info"
-        style="@style/SpeedDialContextMenuItem.NoDrawableTint"/>
-  </LinearLayout>
-</com.android.dialer.speeddial.ContextMenu>
\ 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 080fba5..9a42377 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
@@ -26,17 +26,5 @@
       android:clipToPadding="false"
       android:background="@color/background_dialer_light"
       android:paddingBottom="@dimen/floating_action_button_list_bottom_padding"/>
-
-  <FrameLayout
-      android:id="@+id/context_menu_background"
-      android:layout_width="match_parent"
-      android:layout_height="match_parent"
-      android:visibility="invisible"/>
-
-  <!-- This menu is visible when you long click on a favorite contact. -->
-  <include
-      android:id="@+id/favorite_contact_context_menu"
-      layout="@layout/context_menu_layout"
-      android:visibility="invisible"/>
 </FrameLayout>
 
diff --git a/java/com/android/dialer/speeddial/res/menu/add_favorite_menu.xml b/java/com/android/dialer/speeddial/res/menu/add_favorite_menu.xml
deleted file mode 100644
index b11c7f5..0000000
--- a/java/com/android/dialer/speeddial/res/menu/add_favorite_menu.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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
- -->
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
-  <item
-      android:id="@+id/action_search"
-      android:title="@android:string/search_go"
-      app:showAsAction="always"
-      app:actionViewClass="android.support.v7.widget.SearchView"/>
-</menu>
\ No newline at end of file
diff --git a/java/com/android/dialer/speeddial/res/menu/starred_contact_context_menu.xml b/java/com/android/dialer/speeddial/res/menu/starred_contact_context_menu.xml
new file mode 100644
index 0000000..0143498
--- /dev/null
+++ b/java/com/android/dialer/speeddial/res/menu/starred_contact_context_menu.xml
@@ -0,0 +1,52 @@
+<?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
+ -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+  <item
+      android:id="@+id/starred_contact_context_menu_title"
+      android:enabled="false"
+      android:title=""/>
+
+  <item
+      android:id="@+id/voice_call_container"
+      android:title="@string/contact_menu_voice_call"
+      android:icon="@drawable/quantum_ic_phone_vd_theme_24"
+      android:iconTint="@color/secondary_text_color"/>
+
+  <item
+      android:id="@+id/video_call_container"
+      android:title="@string/contact_menu_video_call"
+      android:icon="@drawable/quantum_ic_videocam_vd_theme_24"
+      android:iconTint="@color/secondary_text_color"/>
+
+  <item
+      android:id="@+id/send_message_container"
+      android:title="@string/contact_menu_message"
+      android:icon="@drawable/quantum_ic_message_vd_theme_24"
+      android:iconTint="@color/secondary_text_color"/>
+
+  <item
+      android:id="@+id/remove_container"
+      android:title="@string/contact_menu_remove"
+      android:icon="@drawable/quantum_ic_close_vd_theme_24"
+      android:iconTint="@color/secondary_text_color"/>
+
+  <item
+      android:id="@+id/contact_info_container"
+      android:title="@string/contact_menu_contact_info"
+      android:icon="@drawable/context_menu_contact_icon"
+      android:iconTint="#FFFFFF"/>
+</menu>
\ No newline at end of file