From 3b735de1525313b0517473aff849914b1722a445 Mon Sep 17 00:00:00 2001 From: calderwoodra Date: Thu, 18 Jan 2018 14:03:17 -0800 Subject: Fixed regression with search not reacting properly to on touch events. There are a few interesting UX niceties that we support in search: 1) When in regular search with an empty query, close the UI if the user touches the blank space. 2) When in regular search with a non-empty query, hide the keyboard if the user touches the the list so they can see all results. 2) When in dialpad search with an empty query, close the UI if the user touches the blank space. 3) When in dialpad search with a non-empty query, hide the dialpad so the user can see the full list of results. This change also adds logic to transfer the dialpad query to the search bar. Bug: 64655802 Test: MainActivityIntegrationTest PiperOrigin-RevId: 182434126 Change-Id: Iabb73b0018fa20e2811010a73a35d3aa3b35343b --- .../dialer/dialpadview/DialpadFragment.java | 3 +- .../com/android/dialer/main/impl/MainActivity.java | 6 ++-- .../dialer/main/impl/MainSearchController.java | 40 +++++++++++++++++++--- .../dialer/main/impl/toolbar/MainToolbar.java | 16 +++++++++ .../dialer/main/impl/toolbar/SearchBarView.java | 27 +++++++++++++++ .../searchfragment/list/NewSearchFragment.java | 2 +- 6 files changed, 84 insertions(+), 10 deletions(-) (limited to 'java/com') diff --git a/java/com/android/dialer/dialpadview/DialpadFragment.java b/java/com/android/dialer/dialpadview/DialpadFragment.java index 2796fca77..9cee7bc2f 100644 --- a/java/com/android/dialer/dialpadview/DialpadFragment.java +++ b/java/com/android/dialer/dialpadview/DialpadFragment.java @@ -59,7 +59,6 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; @@ -415,7 +414,7 @@ public class DialpadFragment extends Fragment .setOnTouchListener( (v, event) -> { if (isDigitsEmpty()) { - if (getActivity() != null && event.getAction() == MotionEvent.ACTION_UP) { + if (getActivity() != null) { LogUtil.i("DialpadFragment.onCreateView", "dialpad spacer touched"); return ((HostInterface) getActivity()).onDialpadSpacerTouchWithEmptyQuery(); } diff --git a/java/com/android/dialer/main/impl/MainActivity.java b/java/com/android/dialer/main/impl/MainActivity.java index 35597f949..7aae8cb7d 100644 --- a/java/com/android/dialer/main/impl/MainActivity.java +++ b/java/com/android/dialer/main/impl/MainActivity.java @@ -157,13 +157,13 @@ public final class MainActivity extends AppCompatActivity @Override // DialpadFragment.HostInterface public boolean onDialpadSpacerTouchWithEmptyQuery() { - searchController.onBackPressed(); - return true; + // No-op, just let the clicks fall through to the search list + return false; } @Override // SearchFragmentListener public void onSearchListTouch() { - searchController.onBackPressed(); + searchController.onSearchListTouch(); } @Override // SearchFragmentListener diff --git a/java/com/android/dialer/main/impl/MainSearchController.java b/java/com/android/dialer/main/impl/MainSearchController.java index 4cccbf4bd..76c93bb87 100644 --- a/java/com/android/dialer/main/impl/MainSearchController.java +++ b/java/com/android/dialer/main/impl/MainSearchController.java @@ -16,6 +16,7 @@ package com.android.dialer.main.impl; +import android.app.FragmentTransaction; import android.support.annotation.Nullable; import android.support.design.widget.FloatingActionButton; import android.text.TextUtils; @@ -24,12 +25,14 @@ import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import com.android.dialer.callintent.CallInitiationType; import com.android.dialer.common.Assert; +import com.android.dialer.common.LogUtil; import com.android.dialer.dialpadview.DialpadFragment; import com.android.dialer.dialpadview.DialpadFragment.DialpadListener; import com.android.dialer.dialpadview.DialpadFragment.OnDialpadQueryChangedListener; import com.android.dialer.main.impl.toolbar.MainToolbar; import com.android.dialer.main.impl.toolbar.SearchBarListener; import com.android.dialer.searchfragment.list.NewSearchFragment; +import com.android.dialer.searchfragment.list.NewSearchFragment.SearchFragmentListener; import com.android.dialer.util.ViewUtil; import com.google.common.base.Optional; @@ -77,8 +80,7 @@ final class MainSearchController implements SearchBarListener { toolbar.expand(animate, Optional.absent()); mainActivity.setTitle(R.string.dialpad_activity_title); - android.app.FragmentTransaction transaction = - mainActivity.getFragmentManager().beginTransaction(); + FragmentTransaction transaction = mainActivity.getFragmentManager().beginTransaction(); // Show Search if (getSearchFragment() == null) { @@ -105,6 +107,7 @@ final class MainSearchController implements SearchBarListener { fab.show(); toolbar.slideDown(animate); + toolbar.transferQueryFromDialpad(getDialpadFragment().getQuery()); mainActivity.setTitle(R.string.main_activity_label); DialpadFragment dialpadFragment = getDialpadFragment(); @@ -154,6 +157,32 @@ final class MainSearchController implements SearchBarListener { getDialpadFragment().slideUp(true); } + /** + * @see SearchFragmentListener#onSearchListTouch() + *

There are 4 scenarios we support to provide a nice UX experience: + *

    + *
  1. When the dialpad is visible with an empty query, close the search UI. + *
  2. When the dialpad is visible with a non-empty query, hide the dialpad. + *
  3. When the regular search UI is visible with an empty query, close the search UI. + *
  4. When the regular search UI is visible with a non-empty query, hide the keyboard. + *
+ */ + public void onSearchListTouch() { + if (isDialpadVisible()) { + if (TextUtils.isEmpty(getDialpadFragment().getQuery())) { + closeSearch(true); + } else { + hideDialpad(/* animate=*/ true, /* bottomNavVisible=*/ false); + } + } else if (isSearchVisible()) { + if (TextUtils.isEmpty(toolbar.getQuery())) { + closeSearch(true); + } else { + toolbar.hideKeyboard(); + } + } + } + /** * Should be called when the user presses the back button. * @@ -161,9 +190,11 @@ final class MainSearchController implements SearchBarListener { */ public boolean onBackPressed() { if (isDialpadVisible() && !TextUtils.isEmpty(getDialpadFragment().getQuery())) { + LogUtil.i("MainSearchController#onBackPressed", "Dialpad visible with query"); hideDialpad(/* animate=*/ true, /* bottomNavVisible=*/ false); return true; } else if (isSearchVisible()) { + LogUtil.i("MainSearchController#onBackPressed", "Search is visible"); closeSearch(true); return true; } else { @@ -218,9 +249,10 @@ final class MainSearchController implements SearchBarListener { public void onSearchBarClicked() { fab.hide(); toolbar.expand(/* animate=*/ true, Optional.absent()); + toolbar.showKeyboard(); + hideBottomNav(); - android.app.FragmentTransaction transaction = - mainActivity.getFragmentManager().beginTransaction(); + FragmentTransaction transaction = mainActivity.getFragmentManager().beginTransaction(); // Show Search if (getSearchFragment() == null) { diff --git a/java/com/android/dialer/main/impl/toolbar/MainToolbar.java b/java/com/android/dialer/main/impl/toolbar/MainToolbar.java index 388815a1e..6e38d2e64 100644 --- a/java/com/android/dialer/main/impl/toolbar/MainToolbar.java +++ b/java/com/android/dialer/main/impl/toolbar/MainToolbar.java @@ -111,4 +111,20 @@ public final class MainToolbar extends Toolbar implements OnMenuItemClickListene public boolean isSlideUp() { return isSlideUp; } + + public String getQuery() { + return searchBar.getQuery(); + } + + public void transferQueryFromDialpad(String query) { + searchBar.setQueryWithoutUpdate(query); + } + + public void hideKeyboard() { + searchBar.hideKeyboard(); + } + + public void showKeyboard() { + searchBar.showKeyboard(); + } } diff --git a/java/com/android/dialer/main/impl/toolbar/SearchBarView.java b/java/com/android/dialer/main/impl/toolbar/SearchBarView.java index 306a5bb4e..6b9f39dc9 100644 --- a/java/com/android/dialer/main/impl/toolbar/SearchBarView.java +++ b/java/com/android/dialer/main/impl/toolbar/SearchBarView.java @@ -30,6 +30,7 @@ import android.view.View; import android.widget.EditText; import android.widget.FrameLayout; import com.android.dialer.animation.AnimUtils; +import com.android.dialer.common.UiUtil; import com.android.dialer.util.DialerUtils; import com.google.common.base.Optional; @@ -44,6 +45,9 @@ final class SearchBarView extends FrameLayout { private SearchBarListener listener; private EditText searchBox; + // This useful for when the query didn't actually change. We want to avoid making excessive calls + // where we can since IPCs can take a long time on slow networks. + private boolean skipLatestTextChange; private int initialHeight; private boolean isExpanded; @@ -177,6 +181,24 @@ final class SearchBarView extends FrameLayout { this.listener = listener; } + public String getQuery() { + return searchBox.getText().toString(); + } + + public void setQueryWithoutUpdate(String query) { + skipLatestTextChange = true; + searchBox.setText(query); + searchBox.setSelection(searchBox.getText().length()); + } + + public void hideKeyboard() { + UiUtil.hideKeyboardFrom(getContext(), searchBox); + } + + public void showKeyboard() { + UiUtil.openKeyboardFrom(getContext(), searchBox); + } + /** Handles logic for text changes in the search box. */ private class SearchBoxTextWatcher implements TextWatcher { @@ -189,6 +211,11 @@ final class SearchBarView extends FrameLayout { @Override public void afterTextChanged(Editable s) { clearButton.setVisibility(TextUtils.isEmpty(s) ? GONE : VISIBLE); + if (skipLatestTextChange) { + skipLatestTextChange = false; + return; + } + listener.onSearchQueryUpdated(s.toString()); } } diff --git a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java index e8a8a4e06..30949d38f 100644 --- a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java +++ b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java @@ -490,8 +490,8 @@ public final class NewSearchFragment extends Fragment public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) { v.performClick(); - FragmentUtils.getParentUnsafe(this, SearchFragmentListener.class).onSearchListTouch(); } + FragmentUtils.getParentUnsafe(this, SearchFragmentListener.class).onSearchListTouch(); return false; } -- cgit v1.2.3