summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/speeddial
diff options
context:
space:
mode:
authorcalderwoodra <calderwoodra@google.com>2018-05-02 12:33:06 -0700
committerWeijia Xu <weijiaxu@google.com>2018-05-04 06:15:45 +0000
commitd22ef290c45701ef65d9d0e004a64c683c8c2788 (patch)
treee7a3a892e171451b8ca891b0291340e46fc448e4 /java/com/android/dialer/speeddial
parent9c70e4ca34c2f3f0c5d48b549602603eed7a6c2e (diff)
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
Diffstat (limited to 'java/com/android/dialer/speeddial')
-rw-r--r--java/com/android/dialer/speeddial/ContextMenu.java156
-rw-r--r--java/com/android/dialer/speeddial/SpeedDialFragment.java61
-rw-r--r--java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java21
-rw-r--r--java/com/android/dialer/speeddial/res/layout/context_menu_layout.xml93
-rw-r--r--java/com/android/dialer/speeddial/res/layout/fragment_speed_dial.xml12
-rw-r--r--java/com/android/dialer/speeddial/res/menu/add_favorite_menu.xml24
-rw-r--r--java/com/android/dialer/speeddial/res/menu/starred_contact_context_menu.xml52
7 files changed, 165 insertions, 254 deletions
diff --git a/java/com/android/dialer/speeddial/ContextMenu.java b/java/com/android/dialer/speeddial/ContextMenu.java
index 09505ab99..e0a4551db 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 {
-
- private ContextMenuItemListener listener;
-
- private TextView voiceView;
- private TextView videoView;
- private TextView smsView;
-
- private SpeedDialUiItem speedDialUiItem;
- private Channel voiceChannel;
- private Channel videoChannel;
-
- public ContextMenu(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
+/** {@link PopupMenu} which presents contact options for starred contacts. */
+public class ContextMenu extends PopupMenu implements OnMenuItemClickListener {
+
+ private final ContextMenuItemListener listener;
+
+ private final SpeedDialUiItem speedDialUiItem;
+ private final Channel voiceChannel;
+ private final Channel videoChannel;
+
+ private boolean visible;
+
+ /**
+ * 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));
- }
-
- private void placeVideoCall() {
- listener.placeCall(Assert.isNotNull(videoChannel));
+ @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;
}
+ @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 702472c6a..f0ba186cd 100644
--- a/java/com/android/dialer/speeddial/SpeedDialFragment.java
+++ b/java/com/android/dialer/speeddial/SpeedDialFragment.java
@@ -32,7 +32,6 @@ import android.support.v7.widget.helper.ItemTouchHelper;
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 @@ public class SpeedDialFragment extends Fragment {
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 @@ public class SpeedDialFragment extends Fragment {
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 @@ public class SpeedDialFragment extends Fragment {
@Override
public void onPause() {
super.onPause();
- contextMenu.hideMenu();
- contextMenuBackground.setVisibility(View.GONE);
+ favoritesListener.hideMenu();
Futures.addCallback(
DialerExecutorComponent.get(getContext())
.backgroundExecutor()
@@ -217,15 +201,6 @@ public class SpeedDialFragment extends Fragment {
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 @@ public class SpeedDialFragment extends Fragment {
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 @@ public class SpeedDialFragment extends Fragment {
@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 @@ public class SpeedDialFragment extends Fragment {
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 325af238a..f730d12a0 100644
--- a/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java
+++ b/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java
@@ -222,16 +222,29 @@ public abstract class SpeedDialUiItem {
*/
@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 a59fa07c7..000000000
--- 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 080fba5d3..9a42377be 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 b11c7f5d6..000000000
--- 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 000000000..0143498e1
--- /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