Merge changes I1309d8df,Ib38ba82e

* changes:
  Updated toolbar and dialpad interactions in NUI.
  Take voicemail alert position into account for "older" header for NUI Voicemail Tab
diff --git a/java/com/android/dialer/app/DialtactsActivity.java b/java/com/android/dialer/app/DialtactsActivity.java
index 65965f4..7592c66 100644
--- a/java/com/android/dialer/app/DialtactsActivity.java
+++ b/java/com/android/dialer/app/DialtactsActivity.java
@@ -1450,18 +1450,16 @@
   }
 
   @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 07e401c..2796fca 100644
--- a/java/com/android/dialer/dialpadview/DialpadFragment.java
+++ b/java/com/android/dialer/dialpadview/DialpadFragment.java
@@ -59,6 +59,7 @@
 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 @@
   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 @@
 
     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 @@
         .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 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 @@
     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 8bdb295..8aac9e8 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.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.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 @@
   }
 
   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 @@
     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 @@
       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 @@
     }
 
     /** 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 @@
           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 @@
           });
     }
 
+    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 @@
       }
     }
 
-    /** 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,21 +308,51 @@
     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 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 onSearchQueryUpdated(String query) {}
 
     @Override
-    public void onSearchBackButtonClicked() {}
-
-    @Override
     public void onVoiceButtonClicked(VoiceSearchResultCallback voiceSearchResultCallback) {}
 
     @Override
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 0b0652a..eb0d45e 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 b322172..388815a 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.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 @@
   private static final AccelerateDecelerateInterpolator SLIDE_INTERPOLATOR =
       new AccelerateDecelerateInterpolator();
 
+  private SearchBarView searchBar;
   private SearchBarListener listener;
   private boolean isSlideUp;
 
@@ -49,6 +52,8 @@
     overflowMenu.setOnMenuItemClickListener(this);
     optionsMenuButton.setOnClickListener(v -> overflowMenu.show());
     optionsMenuButton.setOnTouchListener(overflowMenu.getDragToOpenListener());
+
+    searchBar = findViewById(R.id.search_view_container);
   }
 
   @Override
@@ -69,6 +74,10 @@
   /** 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 @@
         .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 @@
         .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 32258d9..2e31997 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 @@
 /** 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 35c3cee..306a5bb 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 @@
     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 @@
   }
 
   /** 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 @@
   }
 
   /** 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 @@
     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 1e63048..e8a8a4e 100644
--- a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
+++ b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
@@ -490,9 +490,9 @@
   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 @@
   /** 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 32c5b69..86d3860 100644
--- a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java
+++ b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java
@@ -150,7 +150,7 @@
         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;