Refine Dialer UI and add call log search function.

Add call log search funtion: add a menu to support search
call log by name and number in the calllog screen and
support hightlight for search result.

Refine empty call log, clear call log and clear dialog style.
Refine call log spinner style.

CRs-Fixed: 985466

Change-Id: I4bf729bd9b0b043b651792353545da9b33aafe58
diff --git a/res/drawable/clear.xml b/res/drawable/clear.xml
new file mode 100644
index 0000000..882ca1e
--- /dev/null
+++ b/res/drawable/clear.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (c) 2016, The Linux Foundation. All rights reserved.
+  ~
+  ~ Redistribution and use in source and binary forms, with or without
+  ~ modification, are permitted provided that the following conditions are
+  ~ met:
+  ~      Redistributions of source code must retain the above copyright
+  ~       notice, this list of conditions and the following disclaimer.
+  ~      Redistributions in binary form must reproduce the above
+  ~       copyright notice, this list of conditions and the following
+  ~       disclaimer in the documentation and/or other materials provided
+  ~       with the distribution.
+  ~      Neither the name of The Linux Foundation nor the names of its
+  ~       contributors may be used to endorse or promote products derived
+  ~       from this software without specific prior written permission.
+  ~
+  ~ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  ~ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  ~ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ~ ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  ~ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  ~ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  ~ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  ~ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  ~ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  ~ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  ~ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  ~
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+       android:height="24dp"
+       android:width="24dp"
+       android:viewportHeight="24"
+       android:viewportWidth="24">
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41z"/>
+</vector>
diff --git a/res/drawable/color_cursor.xml b/res/drawable/color_cursor.xml
new file mode 100644
index 0000000..be96253
--- /dev/null
+++ b/res/drawable/color_cursor.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (c) 2016, The Linux Foundation. All rights reserved.
+  ~
+  ~ Redistribution and use in source and binary forms, with or without
+  ~ modification, are permitted provided that the following conditions are
+  ~ met:
+  ~      Redistributions of source code must retain the above copyright
+  ~       notice, this list of conditions and the following disclaimer.
+  ~      Redistributions in binary form must reproduce the above
+  ~       copyright notice, this list of conditions and the following
+  ~       disclaimer in the documentation and/or other materials provided
+  ~       with the distribution.
+  ~      Neither the name of The Linux Foundation nor the names of its
+  ~       contributors may be used to endorse or promote products derived
+  ~       from this software without specific prior written permission.
+  ~
+  ~ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  ~ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  ~ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ~ ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  ~ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  ~ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  ~ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  ~ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  ~ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  ~ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  ~ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  ~
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <size android:width="2dp"/>
+    <solid android:color="#FFFFFF"/>
+</shape>
diff --git a/res/layout/empty_content_view.xml b/res/layout/empty_content_view.xml
index 97ac4c7..9436d67 100644
--- a/res/layout/empty_content_view.xml
+++ b/res/layout/empty_content_view.xml
@@ -26,8 +26,8 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:gravity="center_horizontal|top"
-        android:textSize="@dimen/empty_list_message_text_size"
-        android:textColor="@color/empty_list_text_color"
+        android:textSize="@dimen/empty_list_message_text_size_for_Marshmallow"
+        android:textColor="@color/no_call_log"
         android:paddingRight="16dp"
         android:paddingLeft="16dp"
         android:paddingTop="8dp"
diff --git a/res/layout/msim_call_log_spinner.xml b/res/layout/msim_call_log_spinner.xml
index 1ae225e..5310ace 100644
--- a/res/layout/msim_call_log_spinner.xml
+++ b/res/layout/msim_call_log_spinner.xml
@@ -37,12 +37,16 @@
     <Spinner
         android:id="@+id/filter_sub_spinner"
         android:layout_width="0dip"
+        android:textSize="@dimen/call_log_spinner_text_size"
+        android:textColor="@color/list_all_call"
         android:layout_height="@dimen/list_section_divider_min_height"
         android:layout_weight="1"
         android:layout_marginTop="5dip" />
     <Spinner
         android:id="@+id/filter_status_spinner"
         android:layout_width="0dip"
+        android:textSize="@dimen/call_log_spinner_text_size"
+        android:textColor="@color/list_all_call"
         android:layout_height="@dimen/list_section_divider_min_height"
         android:layout_weight="2"
         android:layout_marginTop="5dip" />
diff --git a/res/layout/msim_call_log_spinner_item.xml b/res/layout/msim_call_log_spinner_item.xml
index 4afb62a..0b1bcf3 100644
--- a/res/layout/msim_call_log_spinner_item.xml
+++ b/res/layout/msim_call_log_spinner_item.xml
@@ -31,6 +31,8 @@
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
     style="@style/CallLogSpinnerStyle"
     android:layout_width="match_parent"
-    android:layout_height="40dip"
-    android:paddingLeft="8dip"
-    android:paddingRight="8dip" />
+    android:paddingTop="5dp"
+    android:layout_height="50dip"
+    android:paddingLeft="16dip"
+    android:paddingRight="12dip"
+    android:paddingBottom="8dp" />
diff --git a/res/layout/search_action_bar.xml b/res/layout/search_action_bar.xml
new file mode 100644
index 0000000..2953d72
--- /dev/null
+++ b/res/layout/search_action_bar.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="0dip"
+    android:orientation="horizontal"
+    android:layout_height="0dip" >
+
+   <EditText
+        android:layout_weight="1"
+        android:focusable="true"
+        android:focusableInTouchMode="true"
+        android:layout_width="0dp"
+        android:singleLine="true"
+        android:id="@+id/search_view"
+        android:background="@null"
+        android:textCursorDrawable="@drawable/color_cursor"
+        android:layout_height="match_parent"
+        android:hint="@string/calllog_search_hint"
+        android:cursorVisible="true"
+        android:textColorHint="@color/searchview_edittext"
+        android:textColor="@color/actionbar_icon_color"
+        android:inputType="textFilter" />
+      <ImageView
+         android:id="@+id/search_close_button"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:src="@drawable/clear"
+         android:clickable="true"
+         android:contentDescription="@string/description_clear_search"
+         />
+</LinearLayout>
diff --git a/res/menu/call_log_options.xml b/res/menu/call_log_options.xml
index da38d86..6570714 100644
--- a/res/menu/call_log_options.xml
+++ b/res/menu/call_log_options.xml
@@ -15,6 +15,12 @@
 -->
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
     <item
+        android:id="@+id/search_calllog"
+        android:title="@string/calllog_search_hint"
+        android:showAsAction="never"
+        android:orderInCategory="1"/>
+    <item
+        android:layout_height="58dp"
         android:id="@+id/delete_all"
         android:title="@string/call_log_delete_all"
         android:showAsAction="never"
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index e76929a..54ebfc1 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -287,4 +287,7 @@
     <string name="input_number">"输入号码"</string>
     <string name="speed_dial_cancel">"取消"</string>
     <string name="speed_dial_ok">"确定"</string>
+    <string name="no_call_log">没有通话记录</string>
+    <string name="clear">清除</string>
+    <string name="description_clear_search">清除搜索记录</string>
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 38fd6b3..10b689e 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -139,4 +139,7 @@
     <color name="call_detail_footer_text_color">#616161</color>
     <color name="call_detail_footer_icon_tint">@color/call_detail_footer_text_color</color>
 
+    <color name="searchview_edittext">#59ffffff</color>
+    <color name="no_call_log">#42000000</color>
+    <color name="list_all_call">#d3000000</color>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 6c8d61d..d9c425d 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -174,4 +174,8 @@
     <dimen name="call_type_icon_size">12dp</dimen>
 
     <dimen name="tab_unread_count_margin_left">0dp</dimen>
+
+    <dimen name="list_all_calls">200dp</dimen>
+    <dimen name="empty_list_message_text_size_for_Marshmallow">16dp</dimen>
+    <dimen name="call_log_spinner_text_size">16sp</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3c68c0c..0d73d06 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1111,4 +1111,11 @@
 
     <!-- Text displayed when the list of outgoing calls is empty -->
     <string name="recentOutgoing_empty">You have no outgoing calls.</string>
+
+    <!-- Text displayed when the call log is empty. -->
+    <string name="recentCalls_empty">Your call log is empty</string>
+    <string name="calllog_search_hint">"Search call log"</string>
+    <string name="no_call_log">No call log</string>
+    <string name="clear">Clear</string>
+    <string name="description_clear_search">Clear search</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 478d6ba..dbe3244 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -440,12 +440,13 @@
     </style>
 
     <style name="CallLogSpinnerStyle">
-        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
-        <item name="android:textStyle">bold</item>
-        <item name="android:textColor">@android:color/black</item>
+        <item name="android:textSize">@dimen/call_log_spinner_text_size</item>
         <item name="android:gravity">center_vertical</item>
         <item name="android:ellipsize">marquee</item>
         <item name="android:singleLine">true</item>
-        <item name="android:textAllCaps">true</item>
+    </style>
+    <style name="NOCallLOG">
+        <item name="android:textStyle">bold</item>
+        <item name="android:textColor">#000000</item>
     </style>
 </resources>
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 105dfd9..841dfa6 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -274,7 +274,7 @@
                     mListsFragment.getSpeedDialFragment().hasFrequents() && hasContactsPermission);
 
             menu.findItem(R.id.menu_import_export).setVisible(hasContactsPermission);
-            menu.findItem(R.id.menu_add_contact).setVisible(hasContactsPermission);
+            menu.findItem(R.id.menu_add_contact).setVisible(false);
 
             menu.findItem(R.id.menu_history).setVisible(
                     PermissionsUtil.hasPhonePermissions(DialtactsActivity.this));
diff --git a/src/com/android/dialer/calllog/CallLogActivity.java b/src/com/android/dialer/calllog/CallLogActivity.java
index ff34760..cb4b099 100644
--- a/src/com/android/dialer/calllog/CallLogActivity.java
+++ b/src/com/android/dialer/calllog/CallLogActivity.java
@@ -18,6 +18,7 @@
 import android.app.Activity;
 import android.app.Fragment;
 import android.app.FragmentManager;
+import android.app.FragmentTransaction;
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
@@ -29,11 +30,24 @@
 import android.support.v13.app.FragmentPagerAdapter;
 import android.support.v4.view.ViewPager;
 import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBar.LayoutParams;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.view.inputmethod.InputMethodManager;
+import android.text.TextWatcher;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.MotionEvent;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.SearchView;
+import android.util.Log;
 
 import com.android.contacts.common.interactions.TouchPointManager;
 import com.android.contacts.common.list.ViewPagerTabs;
@@ -54,9 +68,12 @@
     private CallLogFragment mMissedCallsFragment;
 
     private MSimCallLogFragment mMSimCallsFragment;
-
+    private CallLogSearchFragment mSearchFragment;
+    private EditText mSearchView;
+    private ImageView mClearButtonView;
+    private boolean mInSearchUi;
     private String[] mTabTitles;
-
+    private String mSearchQuery;
     private static final int TAB_INDEX_ALL = 0;
     private static final int TAB_INDEX_MISSED = 1;
 
@@ -147,13 +164,6 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        if (TelephonyManager.getDefault().isMultiSimEnabled()) {
-            initMSimCallLog();
-            return;
-        }
-
-        setContentView(R.layout.call_log_activity);
-        getWindow().setBackgroundDrawable(null);
 
         final ActionBar actionBar = getSupportActionBar();
         actionBar.setDisplayShowHomeEnabled(true);
@@ -161,6 +171,14 @@
         actionBar.setDisplayShowTitleEnabled(true);
         actionBar.setElevation(0);
 
+        if ( TelephonyManager.getDefault().isMultiSimEnabled()) {
+            initMSimCallLog();
+            return;
+        }
+
+        setContentView(R.layout.call_log_activity);
+        getWindow().setBackgroundDrawable(null);
+
         int startingTab = TAB_INDEX_ALL;
         final Intent intent = getIntent();
         if (intent != null) {
@@ -188,6 +206,16 @@
     }
 
     @Override
+    public void onAttachFragment(Fragment fragment) {
+        if (fragment instanceof CallLogSearchFragment) {
+            if (mViewPagerAdapter != null) {
+                mSearchFragment = (CallLogSearchFragment) fragment;
+                setupSearchUi();
+            }
+        }
+    }
+
+    @Override
     protected void onResume() {
         mIsResumed = true;
         super.onResume();
@@ -198,6 +226,9 @@
     protected void onPause() {
         mIsResumed = false;
         super.onPause();
+        if (mInSearchUi) {
+            exitSearchUi();
+        }
     }
 
     private void initMSimCallLog() {
@@ -226,17 +257,32 @@
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
         final MenuItem itemDeleteAll = menu.findItem(R.id.delete_all);
-        if (mAllCallsFragment != null && itemDeleteAll != null) {
-            // If onPrepareOptionsMenu is called before fragments are loaded, don't do anything.
-            final CallLogAdapter adapter = mAllCallsFragment.getAdapter();
-            itemDeleteAll.setVisible(adapter != null && !adapter.isEmpty());
-        }
+        final MenuItem itemSearchCallLog = menu.findItem(R.id.search_calllog);
 
         if (mMSimCallsFragment != null && itemDeleteAll != null) {
             final CallLogAdapter adapter = mMSimCallsFragment.getAdapter();
             itemDeleteAll.setVisible(adapter != null && !adapter.isEmpty());
         }
-
+        if (mInSearchUi) {
+            if (itemDeleteAll != null) {
+                itemDeleteAll.setVisible(false);
+            }
+            if (itemSearchCallLog != null) {
+                itemSearchCallLog.setVisible(false);
+            }
+        } else {
+            if (mSearchFragment != null && itemSearchCallLog != null) {
+                final CallLogAdapter adapter = mSearchFragment.getAdapter();
+                itemSearchCallLog.setVisible(adapter != null
+                        && !adapter.isEmpty());
+            }
+            // If onPrepareOptionsMenu is called before fragments loaded. Don't do anything.
+            if (mAllCallsFragment != null && itemDeleteAll != null) {
+                // If onPrepareOptionsMenu is called before fragments are loaded, don't do anything.
+                final CallLogAdapter adapter = mAllCallsFragment.getAdapter();
+                itemDeleteAll.setVisible(adapter != null && !adapter.isEmpty());
+            }
+        }
         return true;
     }
 
@@ -254,6 +300,9 @@
         } else if (item.getItemId() == R.id.delete_all) {
             ClearCallLogDialog.show(getFragmentManager());
             return true;
+        } else if (item.getItemId() == R.id.search_calllog){
+            enterSearchUi();
+            return true;
         }
         return super.onOptionsItemSelected(item);
     }
@@ -286,4 +335,193 @@
         }
         return position;
     }
+
+    private void enterSearchUi() {
+        mInSearchUi = true;
+        if (mSearchFragment == null) {
+            addSearchFragment();
+            return;
+        }
+        mSearchFragment.setUserVisibleHint(true);
+        final FragmentTransaction transaction = getFragmentManager()
+                .beginTransaction();
+        transaction.show(mSearchFragment);
+        transaction.commitAllowingStateLoss();
+        getFragmentManager().executePendingTransactions();
+        setupSearchUi();
+    }
+
+    private void setupSearchUi() {
+        if (mSearchView == null) {
+            prepareSearchView();
+        }
+        final ActionBar actionBar = getSupportActionBar();
+        actionBar.setDisplayShowCustomEnabled(true);
+        if (mMSimCallsFragment != null) {
+            updateMSimFragmentVisibility(false);
+        } else {
+            for (int i = 0; i < mViewPagerAdapter.getCount(); i++) {
+                updateFragmentVisibility(i, false /* not visible */);
+            }
+        }
+        mViewPager.setVisibility(View.GONE);
+        if (mViewPagerTabs != null) {
+            mViewPagerTabs.setVisibility(View.GONE);
+        }
+    }
+
+    private void updateFragmentVisibility(int position, boolean visibility) {
+        if (position >= TAB_INDEX_ALL) {
+            final Fragment fragment = getFragmentAt(position);
+            if (fragment != null) {
+                fragment.setMenuVisibility(visibility);
+                fragment.setUserVisibleHint(visibility);
+            }
+        }
+    }
+
+    private void updateMSimFragmentVisibility(boolean visibility) {
+        if (mMSimCallsFragment != null) {
+            mMSimCallsFragment.setMenuVisibility(visibility);
+            mMSimCallsFragment.setUserVisibleHint(visibility);
+        }
+    }
+
+    private Fragment getFragmentAt(int position) {
+        switch (position) {
+        case TAB_INDEX_ALL:
+            return mAllCallsFragment;
+        case TAB_INDEX_MISSED:
+            return mMissedCallsFragment;
+        default:
+            throw new IllegalStateException("Unknown fragment index: "
+                    + position);
+        }
+    }
+
+    private void addSearchFragment() {
+        if (mSearchFragment != null) {
+            return;
+        }
+        final FragmentTransaction ft = getFragmentManager().beginTransaction();
+        final Fragment searchFragment = new CallLogSearchFragment();
+        searchFragment.setUserVisibleHint(false);
+        ft.add(R.id.calllog_frame, searchFragment);
+        ft.commitAllowingStateLoss();
+    }
+
+    private void prepareSearchView() {
+        final View searchViewLayout = getLayoutInflater().inflate(
+                R.layout.search_action_bar, null);
+        mSearchView = (EditText) searchViewLayout
+                .findViewById(R.id.search_view);
+        mClearButtonView = (ImageView)searchViewLayout.findViewById(R.id.search_close_button);
+        mClearButtonView.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mSearchView.setText("");
+            }
+        });
+        mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
+        mClearButtonView.setVisibility(View.GONE);
+        mSearchView.setOnFocusChangeListener(new OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                if (hasFocus) {
+                    showInputMethod(v.findFocus());
+                } else {
+                    hideInputMethod(v);
+                }
+            }
+        });
+        getSupportActionBar().setCustomView(
+                searchViewLayout,
+                new LayoutParams(LayoutParams.MATCH_PARENT,
+                        LayoutParams.WRAP_CONTENT));
+    }
+
+    private void showInputMethod(View view) {
+        InputMethodManager imm = (InputMethodManager) getSystemService(
+                Context.INPUT_METHOD_SERVICE);
+        imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
+    }
+
+    private void hideInputMethod(View view) {
+        InputMethodManager imm = (InputMethodManager) getSystemService(
+                Context.INPUT_METHOD_SERVICE);
+        if (imm != null && view != null) {
+            imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+        }
+    }
+
+    /**
+     * Listener used to send search queries to the phone search fragment.
+     */
+    private final TextWatcher mPhoneSearchQueryTextListener = new TextWatcher() {
+        @Override
+        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+        }
+
+        @Override
+        public void onTextChanged(CharSequence s, int start, int before, int count) {
+            final String newText = s.toString();
+            if (newText.equals(mSearchQuery)) {
+                // If the query hasn't changed (perhaps due to activity being destroyed
+                // and restored, or user launching the same DIAL intent twice), then there is
+                // no need to do anything here.
+                return;
+            }
+            mSearchQuery = newText;
+            if (mSearchFragment != null) {
+                mClearButtonView.setVisibility(TextUtils.isEmpty(s) ? View.GONE : View.VISIBLE);
+                mSearchFragment.setQueryString(mSearchQuery);
+            }
+        }
+        @Override
+        public void afterTextChanged(Editable e) {
+        }
+    };
+
+    @Override
+    public void onBackPressed() {
+        if (mInSearchUi) {
+            // We should let the user go back to usual screens with tabs.
+            exitSearchUi();
+        } else {
+            super.onBackPressed();
+        }
+    }
+
+    private void exitSearchUi() {
+        final ActionBar actionBar = getSupportActionBar();
+        if (mSearchFragment != null) {
+            mSearchFragment.setUserVisibleHint(false);
+
+            final FragmentTransaction transaction = getFragmentManager()
+                    .beginTransaction();
+            transaction.remove(mSearchFragment);
+            mSearchFragment = null;
+            transaction.commitAllowingStateLoss();
+        }
+
+        // We want to hide SearchView and show Tabs. Also focus on previously
+        // selected one.
+        actionBar.setDisplayShowCustomEnabled(false);
+        if (mMSimCallsFragment != null) {
+            updateMSimFragmentVisibility(true);
+        } else {
+            for (int i = 0; i < mViewPagerAdapter.getCount(); i++) {
+                updateFragmentVisibility(i, i == mViewPager.getCurrentItem());
+            }
+        }
+        mViewPager.setVisibility(View.VISIBLE);
+        if (mViewPagerTabs != null) {
+            mViewPagerTabs.setVisibility(View.VISIBLE);
+        }
+        hideInputMethod(getCurrentFocus());
+        invalidateOptionsMenu();
+        mSearchView.clearFocus();
+        mInSearchUi = false;
+    }
+
 }
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index 85f6a87..f55e131 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -65,6 +65,7 @@
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.regex.Pattern;
 
 /**
  * Adapter class to fill in data for the Call Log.
@@ -108,6 +109,7 @@
     private final Map<String, Boolean> mBlockedNumberCache = new ArrayMap<>();
 
     protected ContactInfoCache mContactInfoCache;
+    private String mFilterString;
 
     private final int mActivityType;
 
@@ -496,7 +498,10 @@
 
         int count = getGroupSize(position);
 
-        final String number = c.getString(CallLogQuery.NUMBER);
+        final String phoneNumber = c.getString(CallLogQuery.NUMBER);
+        Pattern pattern = Pattern.compile("[,;]");
+        String[] num = pattern.split(phoneNumber);
+        final String number = num[0];
         final String countryIso = c.getString(CallLogQuery.COUNTRY_ISO);
         final String postDialDigits = CompatUtils.isNCompatible()
                 && mActivityType != ACTIVITY_TYPE_ARCHIVE ?
@@ -601,7 +606,7 @@
             views.voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
         }
 
-        mCallLogListItemHelper.setPhoneCallDetails(views, details);
+        mCallLogListItemHelper.setPhoneCallDetails(views, details, mFilterString);
 
         if (mCurrentlyExpandedRowId == views.rowId) {
             // In case ViewHolders were added/removed, update the expanded position if the rowIds
@@ -613,7 +618,7 @@
         }
         views.updatePhoto();
 
-        mCallLogListItemHelper.setPhoneCallDetails(views, details);
+        mCallLogListItemHelper.setPhoneCallDetails(views, details, mFilterString);
     }
 
     private String getPreferredDisplayName(ContactInfo contactInfo) {
@@ -924,4 +929,8 @@
         PromoCardViewHolder viewHolder = PromoCardViewHolder.create(view);
         return viewHolder;
     }
+
+    public void setQueryString(String filter) {
+        mFilterString = filter;
+    }
 }
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index 67b72a5..3414129 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -82,8 +82,8 @@
 
     private RecyclerView mRecyclerView;
     private LinearLayoutManager mLayoutManager;
-    private CallLogAdapter mAdapter;
-    private CallLogQueryHandler mCallLogQueryHandler;
+    protected CallLogAdapter mAdapter;
+    protected CallLogQueryHandler mCallLogQueryHandler;
     private boolean mScrollToTop;
 
 
@@ -298,7 +298,7 @@
         mLayoutManager = new LinearLayoutManager(getActivity());
         mRecyclerView.setLayoutManager(mLayoutManager);
         mEmptyListView = (EmptyContentView) view.findViewById(R.id.empty_list_view);
-        mEmptyListView.setImage(R.drawable.empty_call_log);
+        //mEmptyListView.setImage(R.drawable.empty_call_log);
         mEmptyListView.setActionClickedListener(this);
 
         int activityType = mIsCallLogActivity ? CallLogAdapter.ACTIVITY_TYPE_CALL_LOG :
@@ -414,7 +414,11 @@
                 messageId = R.string.call_log_voicemail_empty;
                 break;
             case CallLogQueryHandler.CALL_TYPE_ALL:
-                messageId = R.string.call_log_all_empty;
+                if (mIsCallLogActivity) {
+                    messageId = R.string.no_call_log;
+                } else {
+                    messageId = R.string.recentCalls_empty;
+                }
                 break;
             default:
                 throw new IllegalArgumentException("Unexpected filter type in CallLogFragment: "
@@ -424,6 +428,7 @@
         if (mIsCallLogActivity) {
             mEmptyListView.setActionLabel(EmptyContentView.NO_LABEL);
         } else if (filterType == CallLogQueryHandler.CALL_TYPE_ALL) {
+            mEmptyListView.setImage(R.drawable.empty_call_log);
             mEmptyListView.setActionLabel(R.string.call_log_all_empty_action);
         }
     }
diff --git a/src/com/android/dialer/calllog/CallLogListItemHelper.java b/src/com/android/dialer/calllog/CallLogListItemHelper.java
index 07e2bb4..0aa9fdc 100644
--- a/src/com/android/dialer/calllog/CallLogListItemHelper.java
+++ b/src/com/android/dialer/calllog/CallLogListItemHelper.java
@@ -63,8 +63,8 @@
      */
     public void setPhoneCallDetails(
             CallLogListItemViewHolder views,
-            PhoneCallDetails details) {
-        mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details);
+            PhoneCallDetails details, String filter) {
+        mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details, filter);
 
         // Set the accessibility text for the contact badge
         views.quickContactView.setContentDescription(getContactBadgeDescription(details));
diff --git a/src/com/android/dialer/calllog/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java
index d3dcdd0..f026420 100644
--- a/src/com/android/dialer/calllog/CallLogQueryHandler.java
+++ b/src/com/android/dialer/calllog/CallLogQueryHandler.java
@@ -175,6 +175,20 @@
         fetchCalls(callType, 0);
     }
 
+    public void fetchCalls(String filter) {
+        cancelFetch();
+        fetchCalls(QUERY_CALLLOG_TOKEN, filter);
+    }
+
+    public void fetchCalls(int token, String filter) {
+        String selection = "(" + Calls.NUMBER + " like '%" + filter
+                + "%'  or  " + Calls.CACHED_NAME + " like '%" + filter + "%' )";
+
+        startQuery(token, null, Calls.CONTENT_URI_WITH_VOICEMAIL,
+                CallLogQuery._PROJECTION, selection, null,
+                Calls.DEFAULT_SORT_ORDER);
+    }
+
     public void fetchVoicemailStatus() {
         if (TelecomUtil.hasReadWriteVoicemailPermissions(mContext)) {
             startQuery(QUERY_VOICEMAIL_STATUS_TOKEN, null, Status.CONTENT_URI,
diff --git a/src/com/android/dialer/calllog/CallLogSearchFragment.java b/src/com/android/dialer/calllog/CallLogSearchFragment.java
new file mode 100644
index 0000000..4c8db97
--- /dev/null
+++ b/src/com/android/dialer/calllog/CallLogSearchFragment.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016, The Linux Foundation. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+          copyright notice, this list of conditions and the following
+          disclaimer in the documentation and/or other materials provided
+          with the distribution.
+ * Neither the name of The Linux Foundation, Inc. nor the names of its
+          contributors may be used to endorse or promote products derived
+          from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.dialer.calllog;
+
+import android.app.ListFragment;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.provider.CallLog.Calls;
+
+import com.android.contacts.common.GeoUtil;
+import com.android.dialer.R;
+import com.android.dialerbind.ObjectFactory;
+
+
+public class CallLogSearchFragment extends CallLogFragment {
+
+    private String mQueryString;
+
+    private void updateCallList(int filterType) {
+        mCallLogQueryHandler.fetchCalls(CallLogQueryHandler.CALL_TYPE_ALL);
+    }
+
+    public void fetchCalls() {
+        if (TextUtils.isEmpty(mQueryString)) {
+            mCallLogQueryHandler.fetchCalls(CallLogQueryHandler.CALL_TYPE_ALL);
+        } else {
+            mCallLogQueryHandler.fetchCalls(mQueryString);
+        }
+    }
+
+    public void startCallsQuery() {
+        mAdapter.setLoading(true);
+        if (TextUtils.isEmpty(mQueryString)) {
+            mCallLogQueryHandler.fetchCalls(CallLogQueryHandler.CALL_TYPE_ALL);
+        } else {
+            mCallLogQueryHandler.fetchCalls(mQueryString);
+        }
+    }
+
+    public void setQueryString(String queryString) {
+        if (!TextUtils.equals(mQueryString, queryString)) {
+            mQueryString = queryString;
+            if (mAdapter != null) {
+                mAdapter.setLoading(true);
+                mAdapter.setQueryString(mQueryString);
+                if (TextUtils.isEmpty(queryString)) {
+                    mCallLogQueryHandler
+                            .fetchCalls(CallLogQueryHandler.CALL_TYPE_ALL);
+                } else {
+                    mCallLogQueryHandler.fetchCalls(queryString);
+                }
+            }
+        }
+    }
+
+}
diff --git a/src/com/android/dialer/calllog/ClearCallLogDialog.java b/src/com/android/dialer/calllog/ClearCallLogDialog.java
index bef5010..457f1df 100644
--- a/src/com/android/dialer/calllog/ClearCallLogDialog.java
+++ b/src/com/android/dialer/calllog/ClearCallLogDialog.java
@@ -88,10 +88,9 @@
         };
         return new AlertDialog.Builder(getActivity())
             .setTitle(R.string.clearCallLogConfirmation_title)
-            .setIconAttribute(android.R.attr.alertDialogIcon)
             .setMessage(R.string.clearCallLogConfirmation)
             .setNegativeButton(android.R.string.cancel, null)
-            .setPositiveButton(android.R.string.ok, okListener)
+            .setPositiveButton(R.string.clear, okListener)
             .setCancelable(true)
             .create();
     }
diff --git a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java b/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
index 47b763b..79e1ebb 100644
--- a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
+++ b/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
@@ -27,8 +27,11 @@
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.support.v4.content.ContextCompat;
 import android.telecom.PhoneAccount;
+import android.text.SpannableString;
+import android.text.Spanned;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
+import android.text.style.StyleSpan;
 import android.view.View;
 import android.widget.TextView;
 
@@ -87,6 +90,10 @@
 
     /** Fills the call details views with content. */
     public void setPhoneCallDetails(PhoneCallDetailsViews views, PhoneCallDetails details) {
+        setPhoneCallDetails(views, details, null);
+    }
+    public void setPhoneCallDetails(PhoneCallDetailsViews views,
+            PhoneCallDetails details, String filter) {
         // Display up to a given number of icons.
         views.callTypeIcons.clear();
         int count = details.callTypes.length;
@@ -149,14 +156,34 @@
             views.callAccountLabel.setVisibility(View.GONE);
         }
 
-        final CharSequence nameText;
-        final CharSequence displayNumber = details.displayNumber;
+        CharSequence nameText;
+        CharSequence displayNumber = details.displayNumber;
+        String phoneNum = (String) details.number;
+        if (!TextUtils.isEmpty(filter) && phoneNum.contains(filter)) {
+            int start, end;
+            start = phoneNum.indexOf(filter);
+            end = start + filter.length();
+            SpannableString result = new SpannableString(phoneNum);
+            result.setSpan(new StyleSpan(Typeface.BOLD), start, end,
+                    Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+            displayNumber = result;
+        }
         if (TextUtils.isEmpty(details.getPreferredName())) {
             nameText = displayNumber;
             // We have a real phone number as "nameView" so make it always LTR
             views.nameView.setTextDirection(View.TEXT_DIRECTION_LTR);
         } else {
             nameText = details.getPreferredName();
+            if (!TextUtils.isEmpty(filter) && nameText.toString().toUpperCase()
+                    .contains(filter.toUpperCase())) {
+                int start,end;
+                start = nameText.toString().toUpperCase().indexOf(filter.toUpperCase());
+                end = start + filter.length();
+                SpannableString style = new SpannableString(nameText);
+                style.setSpan(new StyleSpan(Typeface.BOLD), start, end,
+                        Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+                nameText = style;
+            }
         }
 
         views.nameView.setText(nameText);
diff --git a/src/com/android/dialer/widget/EmptyContentView.java b/src/com/android/dialer/widget/EmptyContentView.java
index 719fd3f..70f0e5a 100644
--- a/src/com/android/dialer/widget/EmptyContentView.java
+++ b/src/com/android/dialer/widget/EmptyContentView.java
@@ -74,6 +74,11 @@
             mDescriptionView.setText(null);
             mDescriptionView.setVisibility(View.GONE);
         } else {
+            if (resourceId == R.string.no_call_log) {
+                mDescriptionView.setText(resourceId);
+                mDescriptionView.setVisibility(View.VISIBLE);
+                mDescriptionView.setPadding(0, 0, 0, 700);
+            }
             mDescriptionView.setText(resourceId);
             mDescriptionView.setVisibility(View.VISIBLE);
         }
diff --git a/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java b/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
index daba428..7108841 100644
--- a/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
+++ b/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
@@ -109,14 +109,14 @@
     public void testSetPhoneCallDetails_ReadVoicemail() {
         PhoneCallDetails details =
                 getPhoneCallDetailsWithTypes(AppCompatConstants.CALLS_VOICEMAIL_TYPE);
-        mHelper.setPhoneCallDetails(mViewHolder, details);
+        mHelper.setPhoneCallDetails(mViewHolder, details, null);
         assertEquals(View.VISIBLE, mViewHolder.voicemailPlaybackView.getVisibility());
     }
 
     public void testSetPhoneCallDetails_UnreadVoicemail() {
         PhoneCallDetails details =
                 getPhoneCallDetailsWithTypes(AppCompatConstants.CALLS_VOICEMAIL_TYPE);
-        mHelper.setPhoneCallDetails(mViewHolder, details);
+        mHelper.setPhoneCallDetails(mViewHolder, details, null);
         assertEquals(View.VISIBLE, mViewHolder.voicemailPlaybackView.getVisibility());
     }
 
@@ -264,7 +264,7 @@
         PhoneCallDetails details = getPhoneCallDetails(
                 number, postDialDigits, presentation, formattedNumber);
         details.callTypes = new int[] {callType};
-        mHelper.setPhoneCallDetails(mViewHolder, details);
+        mHelper.setPhoneCallDetails(mViewHolder, details, null);
     }
 
     private PhoneCallDetails getPhoneCallDetails(