Open search ui on touch, and handle related scenarios.

- Add a touch listener to enter the search ui when the user touches
the search bar.
- Add a key listener to exit the search ui if the user closes the
keyboard while the text input is empty. SearchEditTextLayout was
added to facilitate this..
- Get rid of mFragmentsFrame, and control visibility of the
favorites view by adjusting the alpha of the lists framgent instead,
- Delete (hack) logic to change the background color.
- Enter the search ui whenever the dialpad is shown, and exit if it
is hidden and there is no query.
- Handle showing/hiding the actionbar depending on whether the
overlapping panel is displayed. This solution is a little hack, but
what seems to be a weird frameowrk interaction prevents the desired
solution of showing the action bar and then updating the hide offset.

Bug: 14900155
Change-Id: I4e06e4ed8eae49ef48317c99a556c51ff2a206a5
diff --git a/res/layout/search_edittext.xml b/res/layout/search_edittext.xml
index 236d2bf..f44cc0b 100644
--- a/res/layout/search_edittext.xml
+++ b/res/layout/search_edittext.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
+<view class="com.android.dialer.widget.SearchEditTextLayout"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -45,4 +45,4 @@
         android:clickable="true"
         android:contentDescription="@string/description_start_voice_search"
         android:background="?android:attr/selectableItemBackground" />
-</LinearLayout>
\ No newline at end of file
+</view>
\ No newline at end of file
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index f734aae..23f639f 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -31,4 +31,6 @@
         <attr name="call_log_voicemail_status_action_text_color" format="color" />
     </declare-styleable>
 
+    <declare-styleable name="SearchEditTextLayout" />
+
 </resources>
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index a3d7e50..8fa4ae6 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -40,12 +40,13 @@
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
-import android.util.AttributeSet;
 import android.util.Log;
 import android.view.DragEvent;
+import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnDragListener;
 import android.view.animation.AccelerateInterpolator;
@@ -84,6 +85,7 @@
 import com.android.dialer.list.RemoveView;
 import com.android.dialer.list.SearchFragment;
 import com.android.dialer.list.SmartDialSearchFragment;
+import com.android.dialer.widget.SearchEditTextLayout;
 import com.android.dialerbind.DatabaseHelperManager;
 import com.android.internal.telephony.ITelephony;
 
@@ -160,13 +162,12 @@
     private View mFloatingActionButtonContainer;
     private ImageButton mFloatingActionButton;
 
-    private View mFragmentsFrame;
-
     private int mActionBarHeight;
     private boolean mInDialpadSearch;
     private boolean mInRegularSearch;
     private boolean mClearSearchOnPause;
     private boolean mIsDialpadShown;
+    private boolean mIsExitingSearch;
 
     /**
      * The position of the currently selected tab in the attached {@link ListsFragment}.
@@ -206,9 +207,6 @@
     private DialerDatabaseHelper mDialerDatabaseHelper;
     private DragDropController mDragDropController;
 
-    private int mDialerBackgroundColor;
-    private int mContactListBackgroundColor;
-
     private class OverflowPopupMenu extends PopupMenu {
         public OverflowPopupMenu(Context context, View anchor) {
             super(context, anchor);
@@ -267,13 +265,13 @@
             if (!sameSearchMode) {
                 // call enterSearchUi only if we are switching search modes, or entering
                 // search ui for the first time
-                enterSearchUi(mIsDialpadShown, newText);
+                enterSearchUi(mIsDialpadShown, mSearchQuery);
             }
 
             if (mIsDialpadShown && mSmartDialSearchFragment != null) {
-                mSmartDialSearchFragment.setQueryString(newText, false /* delaySelection */);
+                mSmartDialSearchFragment.setQueryString(mSearchQuery, false /* delaySelection */);
             } else if (mRegularSearchFragment != null) {
-                mRegularSearchFragment.setQueryString(newText, false /* delaySelection */);
+                mRegularSearchFragment.setQueryString(mSearchQuery, false /* delaySelection */);
             }
 
             if (TextUtils.isEmpty(newText)) {
@@ -290,6 +288,34 @@
         }
     };
 
+
+    /**
+     * Open the search UI when the user touches the search text view.
+     */
+    private final View.OnTouchListener mSearchViewOnTouchListener = new View.OnTouchListener() {
+        @Override
+        public boolean onTouch(View v, MotionEvent event) {
+            if (!isInSearchUi()) {
+                enterSearchUi(false /* smartDialSearch */, mSearchView.getText().toString());
+            }
+            return false;
+        }
+    };
+
+    /**
+     * If the search term is empty and the user closes the soft keyboard, close the search UI.
+     */
+    private final View.OnKeyListener mSearchEditTextLayoutListener = new View.OnKeyListener() {
+        @Override
+        public boolean onKey(View v, int keyCode, KeyEvent event) {
+            if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN &&
+                    TextUtils.isEmpty(mSearchView.getText().toString())) {
+                onBackPressed();
+            }
+            return false;
+        }
+    };
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -302,12 +328,15 @@
         actionBar.setCustomView(R.layout.search_edittext);
         actionBar.setDisplayShowCustomEnabled(true);
 
-        final View customView = actionBar.getCustomView();
+        SearchEditTextLayout actionBarView = (SearchEditTextLayout) actionBar.getCustomView();
+        actionBarView.setPreImeKeyListener(mSearchEditTextLayoutListener);
 
-        mSearchView = (EditText) customView.findViewById(R.id.search_view);
+        mSearchView = (EditText) actionBarView.findViewById(R.id.search_view);
         mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
+        mSearchView.setOnTouchListener(mSearchViewOnTouchListener);
 
-        mSearchViewCloseButton = customView.findViewById(R.id.search_close_button);
+
+        mSearchViewCloseButton = actionBarView.findViewById(R.id.search_close_button);
         mSearchViewCloseButton.setOnClickListener(this);
 
         final TypedArray styledAttributes = getTheme().obtainStyledAttributes(
@@ -333,12 +362,6 @@
         parentLayout.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
         parentLayout.setOnDragListener(new LayoutOnDragListener());
 
-        mDialerBackgroundColor = getResources().getColor(R.color.background_dialer_light);
-        mContactListBackgroundColor =
-                getResources().getColor(R.color.contact_list_background_color);
-
-        mFragmentsFrame = findViewById(R.id.dialtacts_frame);
-
         mFloatingActionButtonContainer = findViewById(R.id.floating_action_button_container);
         ViewUtil.setupFloatingActionButton(mFloatingActionButtonContainer, getResources());
 
@@ -393,17 +416,9 @@
         } else if (fragment instanceof SmartDialSearchFragment) {
             mSmartDialSearchFragment = (SmartDialSearchFragment) fragment;
             mSmartDialSearchFragment.setOnPhoneNumberPickerActionListener(this);
-            mSmartDialSearchFragment.setShowEmptyListForNullQuery(true);
-            if (mFragmentsFrame != null) {
-                mFragmentsFrame.setAlpha(1.0f);
-            }
         } else if (fragment instanceof SearchFragment) {
             mRegularSearchFragment = (RegularSearchFragment) fragment;
             mRegularSearchFragment.setOnPhoneNumberPickerActionListener(this);
-            mRegularSearchFragment.setShowEmptyListForNullQuery(true);
-            if (mFragmentsFrame != null) {
-                mFragmentsFrame.setAlpha(1.0f);
-            }
         } else if (fragment instanceof ListsFragment) {
             mListsFragment = (ListsFragment) fragment;
             mListsFragment.addOnPageChangeListener(this);
@@ -524,6 +539,10 @@
         final FragmentTransaction ft = getFragmentManager().beginTransaction();
         ft.show(mDialpadFragment);
         ft.commit();
+
+        if (!isInSearchUi()) {
+            enterSearchUi(true /* isSmartDial */, mSearchQuery);
+        }
     }
 
     /**
@@ -538,12 +557,6 @@
             mDialpadFragment.setYFraction(0);
         }
 
-        if (mListsFragment != null && mListsFragment.isResumed() && mListsFragment.isVisible()) {
-            // If the favorites fragment is showing, fade to blank.
-            mFragmentsFrame.animate().alpha(0.0f);
-            parentLayout.setBackgroundColor(mContactListBackgroundColor);
-        }
-
         updateSearchFragmentPosition();
         getActionBar().hide();
     }
@@ -580,13 +593,8 @@
             commitDialpadFragmentHide();
         }
 
-        if (mListsFragment != null && mListsFragment.isVisible()) {
-            mFragmentsFrame.animate().alpha(1.0f);
-            parentLayout.setBackgroundColor(mDialerBackgroundColor);
-        }
-
         updateSearchFragmentPosition();
-        getActionBar().show();
+        mListsFragment.maybeShowActionBar();
     }
 
     /**
@@ -612,7 +620,7 @@
         }
     }
 
-    private boolean getInSearchUi() {
+    private boolean isInSearchUi() {
         return mInDialpadSearch || mInRegularSearch;
     }
 
@@ -745,7 +753,7 @@
      * Shows the search fragment
      */
     private void enterSearchUi(boolean smartDialSearch, String query) {
-        if (getFragmentManager().isDestroyed()) {
+        if (getFragmentManager().isDestroyed() || mIsExitingSearch) {
             // Weird race condition where fragment is doing work after the activity is destroyed
             // due to talkback being on (b/10209937). Just return since we can't do any
             // constructive here.
@@ -773,19 +781,25 @@
         mInRegularSearch = !smartDialSearch;
 
         SearchFragment fragment = (SearchFragment) getFragmentManager().findFragmentByTag(tag);
+        transaction.setCustomAnimations(android.R.animator.fade_in, 0);
         if (fragment == null) {
             if (smartDialSearch) {
                 fragment = new SmartDialSearchFragment();
             } else {
                 fragment = new RegularSearchFragment();
             }
+            transaction.add(R.id.dialtacts_frame, fragment, tag);
+        } else {
+            transaction.show(fragment);
         }
+
         // DialtactsActivity will provide the options menu
         fragment.setHasOptionsMenu(false);
-        transaction.replace(R.id.dialtacts_frame, fragment, tag);
-        transaction.addToBackStack(null);
+        fragment.setShowEmptyListForNullQuery(true);
         fragment.setQueryString(query, false /* delaySelection */);
         transaction.commit();
+
+        mListsFragment.getView().animate().alpha(0).withLayer();
     }
 
     /**
@@ -796,14 +810,27 @@
         if (getFragmentManager().isDestroyed()) {
             return;
         }
-        // Go all the way back to the favorites fragment, regardless of how many times we
-        // transitioned between search fragments
-        getFragmentManager().popBackStack(0, FragmentManager.POP_BACK_STACK_INCLUSIVE);
+
+        mIsExitingSearch = true;
+
+        mSearchView.setText(null);
+        mDialpadFragment.clearDialpad();
         setNotInSearchUi();
 
-        if (mIsDialpadShown) {
-            mFragmentsFrame.setAlpha(0);
+        final FragmentTransaction transaction = getFragmentManager().beginTransaction();
+        transaction.setCustomAnimations(0, android.R.animator.fade_out);
+
+        if (mSmartDialSearchFragment != null) {
+            transaction.remove(mSmartDialSearchFragment);
         }
+        if (mRegularSearchFragment != null) {
+            transaction.remove(mRegularSearchFragment);
+        }
+        transaction.commit();
+
+        mListsFragment.getView().animate().alpha(1).withLayer();
+
+        mIsExitingSearch = false;
     }
 
     /** Returns an Intent to launch Call Settings screen */
@@ -817,10 +844,11 @@
     @Override
     public void onBackPressed() {
         if (mIsDialpadShown) {
+            if (TextUtils.isEmpty(mSearchQuery)) {
+                exitSearchUi();
+            }
             hideDialpadFragment(true, false);
-        } else if (getInSearchUi()) {
-            mSearchView.setText(null);
-            mDialpadFragment.clearDialpad();
+        } else if (isInSearchUi()) {
             exitSearchUi();
         } else {
             super.onBackPressed();
diff --git a/src/com/android/dialer/list/ListsFragment.java b/src/com/android/dialer/list/ListsFragment.java
index 18fa531..2507065 100644
--- a/src/com/android/dialer/list/ListsFragment.java
+++ b/src/com/android/dialer/list/ListsFragment.java
@@ -89,6 +89,8 @@
     private CallLogAdapter mCallLogAdapter;
     private CallLogQueryHandler mCallLogQueryHandler;
 
+    private boolean mIsPanelOpen = true;
+
     /**
      * Call shortcuts older than this date (persisted in shared preferences) will not show up in
      * at the top of the screen
@@ -143,15 +145,21 @@
                 final int availableActionBarHeight =
                         Math.min(mActionBar.getHeight(), topPaneHeight);
                 mActionBar.setHideOffset(mActionBar.getHeight() - availableActionBarHeight);
+
+                if (!mActionBar.isShowing()) {
+                    mActionBar.show();
+                }
             }
         }
 
         @Override
         public void onPanelOpened(View panel) {
+            mIsPanelOpen = true;
         }
 
         @Override
         public void onPanelClosed(View panel) {
+            mIsPanelOpen = false;
         }
     };
 
@@ -335,6 +343,15 @@
         }
     }
 
+    public void maybeShowActionBar() {
+        // TODO: Try to show the action bar regardless of whether the panel is open, and then update
+        // the offset to show/hide the action bar, instead of updating the whether the action bar is
+        // shown in onPanelSlide.
+        if (mIsPanelOpen) {
+            mActionBar.show();
+        }
+    }
+
     private void setupPaneLayout(OverlappingPaneLayout paneLayout) {
         // TODO: Remove the notion of a capturable view. The entire view be slideable, once
         // the framework better supports nested scrolling.
diff --git a/src/com/android/dialer/widget/SearchEditTextLayout.java b/src/com/android/dialer/widget/SearchEditTextLayout.java
new file mode 100644
index 0000000..40a4e43
--- /dev/null
+++ b/src/com/android/dialer/widget/SearchEditTextLayout.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014 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
+ */
+
+package com.android.dialer.widget;
+
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.LinearLayout;
+
+public class SearchEditTextLayout extends LinearLayout {
+    private OnKeyListener mPreImeKeyListener;
+
+    public SearchEditTextLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void setPreImeKeyListener(OnKeyListener listener) {
+        mPreImeKeyListener = listener;
+    }
+
+    @Override
+    public boolean dispatchKeyEventPreIme(KeyEvent event) {
+        if (mPreImeKeyListener != null) {
+            if (mPreImeKeyListener.onKey(this, event.getKeyCode(), event)) {
+                return true;
+            }
+        }
+        return super.dispatchKeyEventPreIme(event);
+    }
+}
\ No newline at end of file