diff options
Diffstat (limited to 'java')
9 files changed, 194 insertions, 59 deletions
diff --git a/java/com/android/dialer/app/DialtactsActivity.java b/java/com/android/dialer/app/DialtactsActivity.java index 65965f44e..7592c6669 100644 --- a/java/com/android/dialer/app/DialtactsActivity.java +++ b/java/com/android/dialer/app/DialtactsActivity.java @@ -1450,18 +1450,16 @@ public class DialtactsActivity extends TransactionSafeActivity } @Override - public boolean onSearchListTouch(MotionEvent event) { + public void onSearchListTouch() { if (isDialpadShown) { PerformanceReport.recordClick(UiAction.Type.CLOSE_DIALPAD); hideDialpadFragment(true, false); if (TextUtils.isEmpty(dialpadQuery)) { exitSearchUi(); } - return true; } else { UiUtil.hideKeyboardFrom(this, searchEditTextLayout); } - return false; } @Override diff --git a/java/com/android/dialer/dialpadview/DialpadFragment.java b/java/com/android/dialer/dialpadview/DialpadFragment.java index 07e401cab..2796fca77 100644 --- a/java/com/android/dialer/dialpadview/DialpadFragment.java +++ b/java/com/android/dialer/dialpadview/DialpadFragment.java @@ -59,6 +59,7 @@ 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; @@ -140,6 +141,7 @@ public class DialpadFragment extends Fragment private static final String EXTRA_SEND_EMPTY_FLASH = "com.android.phone.extra.SEND_EMPTY_FLASH"; private static final String PREF_DIGITS_FILLED_BY_INTENT = "pref_digits_filled_by_intent"; + private static final String PREF_IS_DIALPAD_SLIDE_OUT = "pref_is_dialpad_slide_out"; private static Optional<String> currentCountryIsoForTesting = Optional.absent(); @@ -346,6 +348,7 @@ public class DialpadFragment extends Fragment if (state != null) { digitsFilledByIntent = state.getBoolean(PREF_DIGITS_FILLED_BY_INTENT); + isDialpadSlideUp = state.getBoolean(PREF_IS_DIALPAD_SLIDE_OUT); } dialpadSlideInDuration = getResources().getInteger(R.integer.dialpad_slide_in_duration); @@ -412,7 +415,7 @@ public class DialpadFragment extends Fragment .setOnTouchListener( (v, event) -> { if (isDigitsEmpty()) { - if (getActivity() != null) { + if (getActivity() != null && event.getAction() == MotionEvent.ACTION_UP) { LogUtil.i("DialpadFragment.onCreateView", "dialpad spacer touched"); return ((HostInterface) getActivity()).onDialpadSpacerTouchWithEmptyQuery(); } @@ -777,6 +780,7 @@ public class DialpadFragment extends Fragment public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(PREF_DIGITS_FILLED_BY_INTENT, digitsFilledByIntent); + outState.putBoolean(PREF_IS_DIALPAD_SLIDE_OUT, isDialpadSlideUp); } @Override @@ -1559,6 +1563,10 @@ public class DialpadFragment extends Fragment getView().startAnimation(slideUp); } + public boolean isDialpadSlideUp() { + return isDialpadSlideUp; + } + /** Returns the text in the dialpad */ public String getQuery() { return digits.getText().toString(); diff --git a/java/com/android/dialer/main/impl/MainActivity.java b/java/com/android/dialer/main/impl/MainActivity.java index 8bdb295c7..8aac9e810 100644 --- a/java/com/android/dialer/main/impl/MainActivity.java +++ b/java/com/android/dialer/main/impl/MainActivity.java @@ -25,6 +25,7 @@ import android.support.design.widget.FloatingActionButton; import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; +import android.view.View; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.widget.ImageView; @@ -42,14 +43,24 @@ import com.android.dialer.main.impl.BottomNavBar.OnBottomNavTabSelectedListener; 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.speeddial.SpeedDialFragment; +import com.android.dialer.util.ViewUtil; import com.android.dialer.voicemail.listui.NewVoicemailFragment; +import com.google.common.base.Optional; /** This is the main activity for dialer. It hosts favorites, call log, search, dialpad, etc... */ public final class MainActivity extends AppCompatActivity - implements OnContactSelectedListener, OnDialpadQueryChangedListener, DialpadListener { + implements OnContactSelectedListener, + OnDialpadQueryChangedListener, + DialpadListener, + DialpadFragment.HostInterface, + SearchFragmentListener { + + private static final String IS_FAB_HIDDEN_KEY = "is_fab_hidden"; private SearchController searchController; + private FloatingActionButton fab; /** * @param context Context of the application package implementing MainActivity class. @@ -70,18 +81,33 @@ public final class MainActivity extends AppCompatActivity } private void initLayout() { - FloatingActionButton fab = findViewById(R.id.fab); + fab = findViewById(R.id.fab); fab.setOnClickListener(v -> searchController.showDialpad(true)); MainToolbar toolbar = findViewById(R.id.toolbar); - toolbar.setSearchBarListener(new MainSearchBarListener()); - searchController = new SearchController(fab, toolbar); setSupportActionBar(findViewById(R.id.toolbar)); BottomNavBar navBar = findViewById(R.id.bottom_nav_bar); navBar.setOnTabSelectedListener(new MainBottomNavBarBottomNavTabListener()); // TODO(calderwoodra): Implement last tab navBar.selectTab(BottomNavBar.TabIndex.SPEED_DIAL); + + searchController = new SearchController(navBar, fab, toolbar); + toolbar.setSearchBarListener(searchController); + } + + @Override + protected void onSaveInstanceState(Bundle bundle) { + super.onSaveInstanceState(bundle); + bundle.putBoolean(IS_FAB_HIDDEN_KEY, !fab.isShown()); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + if (savedInstanceState.getBoolean(IS_FAB_HIDDEN_KEY, false)) { + fab.hide(); + } } @Override @@ -119,31 +145,59 @@ public final class MainActivity extends AppCompatActivity super.onBackPressed(); } - /** Search controller for handling all the logic related to hiding/showing search and dialpad. */ - private final class SearchController { + @Override // DialpadFragment.HostInterface + public boolean onDialpadSpacerTouchWithEmptyQuery() { + searchController.onBackPressed(); + return true; + } + + @Override // SearchFragmentListener + public void onSearchListTouch() { + searchController.onBackPressed(); + } + + @Override // SearchFragmentListener + public void onCallPlacedFromSearch() { + // TODO(calderwoodra): logging + } + + /** + * Search controller for handling all the logic related to entering and exiting the search UI. + * + * <p>Components modified are: + * + * <ul> + * <li>Bottom Nav Bar, completely hidden when in search ui. + * <li>FAB, visible in dialpad search when dialpad is hidden. Otherwise, FAB is hidden. + * <li>Toolbar, expanded and visible when dialpad is hidden. Otherwise, hidden off screen. + * <li>Dialpad, shown through fab clicks and hidden with Android back button. + * </ul> + * + * @see #onBackPressed() + */ + private final class SearchController implements SearchBarListener { private static final String DIALPAD_FRAGMENT_TAG = "dialpad_fragment_tag"; private static final String SEARCH_FRAGMENT_TAG = "search_fragment_tag"; + private final BottomNavBar bottomNav; private final FloatingActionButton fab; private final MainToolbar toolbar; - private boolean isDialpadVisible; - private boolean isSearchVisible; - - private SearchController(FloatingActionButton fab, MainToolbar toolbar) { + private SearchController( + BottomNavBar bottomNav, FloatingActionButton fab, MainToolbar toolbar) { + this.bottomNav = bottomNav; this.fab = fab; this.toolbar = toolbar; } /** Shows the dialpad, hides the FAB and slides the toolbar off screen. */ public void showDialpad(boolean animate) { - Assert.checkArgument(!isDialpadVisible); - isDialpadVisible = true; - isSearchVisible = true; + Assert.checkArgument(!isDialpadVisible()); fab.hide(); toolbar.slideUp(animate); + toolbar.expand(animate, Optional.absent()); setTitle(R.string.dialpad_activity_title); android.app.FragmentTransaction transaction = getFragmentManager().beginTransaction(); @@ -152,14 +206,14 @@ public final class MainActivity extends AppCompatActivity if (getSearchFragment() == null) { NewSearchFragment searchFragment = NewSearchFragment.newInstance(false); transaction.add(R.id.search_fragment_container, searchFragment, SEARCH_FRAGMENT_TAG); - } else if (!isSearchVisible) { + } else if (!isSearchVisible()) { transaction.show(getSearchFragment()); } // Show Dialpad if (getDialpadFragment() == null) { DialpadFragment dialpadFragment = new DialpadFragment(); - transaction.add(R.id.search_fragment_container, dialpadFragment, DIALPAD_FRAGMENT_TAG); + transaction.add(R.id.dialpad_fragment_container, dialpadFragment, DIALPAD_FRAGMENT_TAG); } else { DialpadFragment dialpadFragment = getDialpadFragment(); transaction.show(dialpadFragment); @@ -168,9 +222,8 @@ public final class MainActivity extends AppCompatActivity } /** Hides the dialpad, reveals the FAB and slides the toolbar back onto the screen. */ - public void hideDialpad(boolean animate) { - Assert.checkArgument(isDialpadVisible); - isDialpadVisible = false; + public void hideDialpad(boolean animate, boolean bottomNavVisible) { + Assert.checkArgument(isDialpadVisible()); fab.show(); toolbar.slideDown(animate); @@ -182,7 +235,15 @@ public final class MainActivity extends AppCompatActivity animate, new AnimationListener() { @Override - public void onAnimationStart(Animation animation) {} + public void onAnimationStart(Animation animation) { + // Slide the bottom nav on animation start so it's (not) visible when the dialpad + // finishes animating down. + if (bottomNavVisible) { + showBottomNav(); + } else { + hideBottomNav(); + } + } @Override public void onAnimationEnd(Animation animation) { @@ -196,16 +257,30 @@ public final class MainActivity extends AppCompatActivity }); } + private void hideBottomNav() { + bottomNav.setVisibility(View.INVISIBLE); + if (bottomNav.getHeight() == 0) { + ViewUtil.doOnGlobalLayout(bottomNav, v -> fab.setTranslationY(bottomNav.getHeight())); + } else { + fab.setTranslationY(bottomNav.getHeight()); + } + } + + private void showBottomNav() { + bottomNav.setVisibility(View.VISIBLE); + fab.setTranslationY(0); + } + /** * Should be called when the user presses the back button. * - * @return true if {@link #onBackPressed()} handled to action. + * @return true if #onBackPressed() handled to action. */ public boolean onBackPressed() { - if (isDialpadVisible && !TextUtils.isEmpty(getDialpadFragment().getQuery())) { - hideDialpad(true); + if (isDialpadVisible() && !TextUtils.isEmpty(getDialpadFragment().getQuery())) { + hideDialpad(/* animate=*/ true, /* bottomNavVisible=*/ false); return true; - } else if (isSearchVisible) { + } else if (isSearchVisible()) { closeSearch(true); return true; } else { @@ -213,14 +288,17 @@ public final class MainActivity extends AppCompatActivity } } - /** Calls {@link #hideDialpad(boolean)} and removes the search fragment. */ + /** Calls {@link #hideDialpad(boolean, boolean)} and removes the search fragment. */ private void closeSearch(boolean animate) { - Assert.checkArgument(isSearchVisible); - if (isDialpadVisible) { - hideDialpad(animate); + Assert.checkArgument(isSearchVisible()); + if (isDialpadVisible()) { + hideDialpad(animate, /* bottomNavVisible=*/ true); + } else if (!fab.isShown()) { + fab.show(); } + showBottomNav(); + toolbar.collapse(animate); getFragmentManager().beginTransaction().remove(getSearchFragment()).commit(); - isSearchVisible = false; } private DialpadFragment getDialpadFragment() { @@ -230,19 +308,49 @@ public final class MainActivity extends AppCompatActivity private NewSearchFragment getSearchFragment() { return (NewSearchFragment) getFragmentManager().findFragmentByTag(SEARCH_FRAGMENT_TAG); } - } - /** - * Implementation of {@link SearchBarListener} that holds the logic for how to handle search bar - * events. - */ - private static final class MainSearchBarListener implements SearchBarListener { + private boolean isDialpadVisible() { + DialpadFragment fragment = getDialpadFragment(); + return fragment != null + && fragment.isAdded() + && !fragment.isHidden() + && fragment.isDialpadSlideUp(); + } + + private boolean isSearchVisible() { + NewSearchFragment fragment = getSearchFragment(); + return fragment != null && fragment.isAdded() && !fragment.isHidden(); + } + /** + * Opens search in regular/search bar search mode. + * + * <p>Hides fab, expands toolbar and starts the search fragment. + */ @Override - public void onSearchQueryUpdated(String query) {} + public void onSearchBarClicked() { + fab.hide(); + toolbar.expand(/* animate=*/ true, Optional.absent()); + + android.app.FragmentTransaction transaction = getFragmentManager().beginTransaction(); + + // Show Search + if (getSearchFragment() == null) { + NewSearchFragment searchFragment = NewSearchFragment.newInstance(false); + transaction.add(R.id.search_fragment_container, searchFragment, SEARCH_FRAGMENT_TAG); + } else if (!isSearchVisible()) { + transaction.show(getSearchFragment()); + } + transaction.commit(); + } + + @Override + public void onSearchBackButtonClicked() { + closeSearch(true); + } @Override - public void onSearchBackButtonClicked() {} + public void onSearchQueryUpdated(String query) {} @Override public void onVoiceButtonClicked(VoiceSearchResultCallback voiceSearchResultCallback) {} 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 0b0652af5..eb0d45e57 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 @@ -37,13 +37,20 @@ android:layout_height="wrap_content" android:layout_alignParentBottom="true"/> - <!-- Holds search and dialpad fragments --> + <!-- Holds search fragment --> <FrameLayout android:id="@+id/search_fragment_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/toolbar"/> + <!-- Holds Dialpad fragment --> + <FrameLayout + android:id="@+id/dialpad_fragment_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:elevation="10dp"/> + <!-- MainToolbar --> <include android:id="@+id/toolbar" diff --git a/java/com/android/dialer/main/impl/toolbar/MainToolbar.java b/java/com/android/dialer/main/impl/toolbar/MainToolbar.java index b32217230..388815a1e 100644 --- a/java/com/android/dialer/main/impl/toolbar/MainToolbar.java +++ b/java/com/android/dialer/main/impl/toolbar/MainToolbar.java @@ -25,6 +25,8 @@ import android.view.MenuItem; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.ImageButton; import com.android.dialer.common.Assert; +import com.android.dialer.util.ViewUtil; +import com.google.common.base.Optional; /** Toolbar for {@link com.android.dialer.main.impl.MainActivity}. */ public final class MainToolbar extends Toolbar implements OnMenuItemClickListener { @@ -33,6 +35,7 @@ public final class MainToolbar extends Toolbar implements OnMenuItemClickListene private static final AccelerateDecelerateInterpolator SLIDE_INTERPOLATOR = new AccelerateDecelerateInterpolator(); + private SearchBarView searchBar; private SearchBarListener listener; private boolean isSlideUp; @@ -49,6 +52,8 @@ public final class MainToolbar extends Toolbar implements OnMenuItemClickListene overflowMenu.setOnMenuItemClickListener(this); optionsMenuButton.setOnClickListener(v -> overflowMenu.show()); optionsMenuButton.setOnTouchListener(overflowMenu.getDragToOpenListener()); + + searchBar = findViewById(R.id.search_view_container); } @Override @@ -69,6 +74,10 @@ public final class MainToolbar extends Toolbar implements OnMenuItemClickListene /** Slides the toolbar up and off the screen. */ public void slideUp(boolean animate) { Assert.checkArgument(!isSlideUp); + if (getHeight() == 0) { + ViewUtil.doOnGlobalLayout(this, view -> slideUp(animate)); + return; + } isSlideUp = true; animate() .translationY(-getHeight()) @@ -77,11 +86,7 @@ public final class MainToolbar extends Toolbar implements OnMenuItemClickListene .start(); } - /** - * Slides the toolbar down and back onto the screen. - * - * @param animate - */ + /** Slides the toolbar down and back onto the screen. */ public void slideDown(boolean animate) { Assert.checkArgument(isSlideUp); isSlideUp = false; @@ -92,6 +97,16 @@ public final class MainToolbar extends Toolbar implements OnMenuItemClickListene .start(); } + /** @see SearchBarView#collapse(boolean) */ + public void collapse(boolean animate) { + searchBar.collapse(animate); + } + + /** @see SearchBarView#collapse(boolean) */ + public void expand(boolean animate, Optional<String> text) { + searchBar.expand(animate, text); + } + @VisibleForTesting public boolean isSlideUp() { return isSlideUp; diff --git a/java/com/android/dialer/main/impl/toolbar/SearchBarListener.java b/java/com/android/dialer/main/impl/toolbar/SearchBarListener.java index 32258d95b..2e31997e6 100644 --- a/java/com/android/dialer/main/impl/toolbar/SearchBarListener.java +++ b/java/com/android/dialer/main/impl/toolbar/SearchBarListener.java @@ -19,6 +19,9 @@ package com.android.dialer.main.impl.toolbar; /** Useful callback for {@link SearchBarView} listeners. */ public interface SearchBarListener { + /** Called when the user clicks on the search bar. */ + void onSearchBarClicked(); + /** Called when the search query updates. */ void onSearchQueryUpdated(String query); diff --git a/java/com/android/dialer/main/impl/toolbar/SearchBarView.java b/java/com/android/dialer/main/impl/toolbar/SearchBarView.java index 35c3cee16..306a5bb4e 100644 --- a/java/com/android/dialer/main/impl/toolbar/SearchBarView.java +++ b/java/com/android/dialer/main/impl/toolbar/SearchBarView.java @@ -66,7 +66,7 @@ final class SearchBarView extends FrameLayout { searchBoxCollapsed = findViewById(R.id.search_box_collapsed); searchBoxExpanded = findViewById(R.id.search_box_expanded); - setOnClickListener(v -> expand(true, Optional.absent())); + setOnClickListener(v -> listener.onSearchBarClicked()); findViewById(R.id.voice_search_button).setOnClickListener(v -> voiceSearchClicked()); findViewById(R.id.search_back_button).setOnClickListener(v -> onSearchBackButtonClicked()); clearButton.setOnClickListener(v -> onSearchClearButtonClicked()); @@ -92,7 +92,7 @@ final class SearchBarView extends FrameLayout { } /** Expand the search bar and populate it with text if any exists. */ - private void expand(boolean animate, Optional<String> text) { + /* package-private */ void expand(boolean animate, Optional<String> text) { if (isExpanded) { return; } @@ -126,7 +126,7 @@ final class SearchBarView extends FrameLayout { } /** Collapse the search bar and clear it's text. */ - private void collapse(boolean animate) { + /* package-private */ void collapse(boolean animate) { if (!isExpanded) { return; } @@ -173,7 +173,7 @@ final class SearchBarView extends FrameLayout { requestLayout(); } - public void setSearchBarListener(SearchBarListener listener) { + /* package-private */ void setSearchBarListener(SearchBarListener listener) { this.listener = listener; } diff --git a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java index 1e630488d..e8a8a4e06 100644 --- a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java +++ b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java @@ -490,9 +490,9 @@ 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(); } - return FragmentUtils.getParentUnsafe(this, SearchFragmentListener.class) - .onSearchListTouch(event); + return false; } @Override @@ -540,12 +540,8 @@ public final class NewSearchFragment extends Fragment /** Callback to {@link NewSearchFragment}'s parent to be notified of important events. */ public interface SearchFragmentListener { - /** - * Called when the list view in {@link NewSearchFragment} is touched. - * - * @see OnTouchListener#onTouch(View, MotionEvent) - */ - boolean onSearchListTouch(MotionEvent event); + /** Called when the list view in {@link NewSearchFragment} is clicked. */ + void onSearchListTouch(); /** Called when a call is placed from the search fragment. */ void onCallPlacedFromSearch(); diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java index 32c5b6991..86d386050 100644 --- a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java +++ b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java @@ -150,7 +150,7 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<ViewHolder> this.olderHeaderPosition = Integer.MAX_VALUE; // Didn't find any "Older" rows. } else { this.todayHeaderPosition = Integer.MAX_VALUE; // Didn't find any "Today" rows. - this.olderHeaderPosition = 0; + this.olderHeaderPosition = 0 + alertOffSet; } } else { // There are no rows, just need to set these because they are final. this.todayHeaderPosition = Integer.MAX_VALUE; |