From d22ef290c45701ef65d9d0e004a64c683c8c2788 Mon Sep 17 00:00:00 2001 From: calderwoodra Date: Wed, 2 May 2018 12:33:06 -0700 Subject: 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 --- java/com/android/dialer/speeddial/ContextMenu.java | 156 +++++++++++---------- .../dialer/speeddial/SpeedDialFragment.java | 61 ++------ .../dialer/speeddial/loader/SpeedDialUiItem.java | 21 ++- .../speeddial/res/layout/context_menu_layout.xml | 93 ------------ .../speeddial/res/layout/fragment_speed_dial.xml | 12 -- .../speeddial/res/menu/add_favorite_menu.xml | 24 ---- .../res/menu/starred_contact_context_menu.xml | 52 +++++++ 7 files changed, 165 insertions(+), 254 deletions(-) delete mode 100644 java/com/android/dialer/speeddial/res/layout/context_menu_layout.xml delete mode 100644 java/com/android/dialer/speeddial/res/menu/add_favorite_menu.xml create mode 100644 java/com/android/dialer/speeddial/res/menu/starred_contact_context_menu.xml (limited to 'java/com/android/dialer/speeddial') 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> 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 @@ - - - - - - - - - - - - - - - - - - - - - - \ 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"/> - - - - - 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 @@ - - - - - \ 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 @@ + + + + + + + + + + + + + + + \ No newline at end of file -- cgit v1.2.3