diff options
author | blong <blong@codeaurora.org> | 2014-09-02 17:10:35 +0800 |
---|---|---|
committer | Xiaojing Zhang <zhangx@codeaurora.org> | 2014-11-04 20:35:42 -0800 |
commit | 837b4965ab91525fe42d77c99fb81d178bb95c04 (patch) | |
tree | 3035a18322fc3aa1ac11a5f8f5e31cc18c23396d /src | |
parent | bd3aefdf298ad570053c8c5fd9cf541386198c5e (diff) | |
download | android_packages_apps_Dialer-837b4965ab91525fe42d77c99fb81d178bb95c04.tar.gz android_packages_apps_Dialer-837b4965ab91525fe42d77c99fb81d178bb95c04.tar.bz2 android_packages_apps_Dialer-837b4965ab91525fe42d77c99fb81d178bb95c04.zip |
Add call log search in dialer
- add a menu to support search call log by name and number in
the calllog screen
Change-Id: I80c85a9bdea962296ea39dab7ce689df06e8159e
Diffstat (limited to 'src')
5 files changed, 395 insertions, 15 deletions
diff --git a/src/com/android/dialer/PhoneCallDetailsHelper.java b/src/com/android/dialer/PhoneCallDetailsHelper.java index c5f2fb675..57e2c88f5 100644 --- a/src/com/android/dialer/PhoneCallDetailsHelper.java +++ b/src/com/android/dialer/PhoneCallDetailsHelper.java @@ -20,8 +20,12 @@ import android.content.res.Resources; import android.provider.CallLog; import android.provider.CallLog.Calls; import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.graphics.Typeface; +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; @@ -71,7 +75,13 @@ public class PhoneCallDetailsHelper { } /** Fills the call details views with content. */ - public void setPhoneCallDetails(PhoneCallDetailsViews views, PhoneCallDetails details) { + 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; @@ -110,16 +120,35 @@ public class PhoneCallDetailsHelper { views.callAccountIcon.setVisibility(View.GONE); } - final CharSequence nameText; - final CharSequence displayNumber = + CharSequence nameText; + CharSequence displayNumber = mPhoneNumberHelper.getDisplayNumber(details.number, details.numberPresentation, details.formattedNumber); + 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.name)) { 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.name; + if (!TextUtils.isEmpty(filter) && nameText.toString().contains(filter)) { + int start,end; + start = nameText.toString().indexOf(filter); + 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/calllog/CallLogActivity.java b/src/com/android/dialer/calllog/CallLogActivity.java index a7a8c0549..ba522bcf9 100644 --- a/src/com/android/dialer/calllog/CallLogActivity.java +++ b/src/com/android/dialer/calllog/CallLogActivity.java @@ -16,8 +16,10 @@ package com.android.dialer.calllog; import android.app.ActionBar; +import android.app.ActionBar.LayoutParams; import android.app.Fragment; import android.app.FragmentManager; +import android.app.FragmentTransaction; import android.content.Context; import android.content.Intent; import android.database.Cursor; @@ -28,10 +30,18 @@ import android.provider.CallLog.Calls; import android.telephony.TelephonyManager; import android.support.v13.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; +import android.text.TextUtils; +import android.view.View; +import android.view.View.OnFocusChangeListener; +import android.view.inputmethod.InputMethodManager; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; +import android.widget.SearchView; +import android.widget.SearchView.OnCloseListener; +import android.widget.SearchView.OnQueryTextListener; +import android.util.Log; import com.android.contacts.common.interactions.TouchPointManager; import com.android.contacts.common.list.ViewPagerTabs; @@ -55,7 +65,9 @@ public class CallLogActivity extends AnalyticsActivity implements CallLogQueryHa private boolean mSwitchToVoicemailTab; private MSimCallLogFragment mMSimCallsFragment; - + private CallLogSearchFragment mSearchFragment; + private SearchView mSearchView; + private boolean mInSearchUi; private String[] mTabTitles; private static final int TAB_INDEX_ALL = 0; @@ -147,6 +159,7 @@ public class CallLogActivity extends AnalyticsActivity implements CallLogQueryHa if (getTelephonyManager().isMultiSimEnabled()) { initMSimCallLog(); + addSearchFragment(); return; } @@ -197,7 +210,7 @@ public class CallLogActivity extends AnalyticsActivity implements CallLogQueryHa mViewPagerTabs.setViewPager(mViewPager); mViewPager.setCurrentItem(startingTab); } - + addSearchFragment(); mVoicemailStatusHelper = new VoicemailStatusHelperImpl(); } @@ -213,6 +226,13 @@ public class CallLogActivity extends AnalyticsActivity implements CallLogQueryHa callLogQueryHandler.fetchVoicemailStatus(); } + @Override + public void onAttachFragment(Fragment fragment) { + if (fragment instanceof CallLogSearchFragment) { + mSearchFragment = (CallLogSearchFragment) fragment; + } + } + private TelephonyManager getTelephonyManager() { return (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); } @@ -243,7 +263,20 @@ public class CallLogActivity extends AnalyticsActivity implements CallLogQueryHa @Override public boolean onPrepareOptionsMenu(Menu menu) { final MenuItem itemDeleteAll = menu.findItem(R.id.delete_all); - + final MenuItem itemSearchCallLog = menu.findItem(R.id.search_calllog); + 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) { final CallLogAdapter adapter = mAllCallsFragment.getAdapter(); @@ -254,21 +287,25 @@ public class CallLogActivity extends AnalyticsActivity implements CallLogQueryHa final CallLogAdapter adapter = mMSimCallsFragment.getAdapter(); itemDeleteAll.setVisible(adapter != null && !adapter.isEmpty()); } - + } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case android.R.id.home: - final Intent intent = new Intent(this, DialtactsActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - case R.id.delete_all: - onDelCallLog(); - return true; + case android.R.id.home: + final Intent intent = new Intent(this, DialtactsActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + case R.id.delete_all: + onDelCallLog(); + return true; + case R.id.search_calllog: + enterSearchUi(); + return true; + } return super.onOptionsItemSelected(item); } @@ -278,6 +315,7 @@ public class CallLogActivity extends AnalyticsActivity implements CallLogQueryHa "com.android.contacts.action.MULTI_PICK_CALL"); startActivity(intent); } + @Override public void onVoicemailStatusFetched(Cursor statusCursor) { if (this.isFinishing()) { @@ -306,4 +344,206 @@ public class CallLogActivity extends AnalyticsActivity implements CallLogQueryHa // Return false; did not take ownership of cursor return false; } + + private void enterSearchUi() { + if (mSearchFragment == null) { + return; + } + if (mSearchView == null) { + prepareSearchView(); + } + final ActionBar actionBar = getActionBar(); + + mSearchView.setQuery(null, true); + mSearchView.requestFocus(); + + actionBar.setDisplayShowCustomEnabled(true); + + for (int i = 0; i < mViewPagerAdapter.getCount(); i++) { + updateFragmentVisibility(i, false /* not visible */); + } + + mSearchFragment.setUserVisibleHint(true); + final FragmentTransaction transaction = getFragmentManager() + .beginTransaction(); + transaction.show(mSearchFragment); + transaction.commitAllowingStateLoss(); + getFragmentManager().executePendingTransactions(); + mViewPager.setVisibility(View.GONE); + mViewPagerTabs.setVisibility(View.GONE); + // We need to call this and onActionViewCollapsed() manually, since we + // are using a custom + // layout instead of asking the search menu item to take care of + // SearchView. + mSearchView.onActionViewExpanded(); + mInSearchUi = true; + } + + 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 Fragment getFragmentAt(int position) { + switch (position) { + case TAB_INDEX_ALL: + if (getTelephonyManager().isMultiSimEnabled()) { + return mMSimCallsFragment; + } else { + return mAllCallsFragment; + } + case TAB_INDEX_MISSED: + return mMissedCallsFragment; + case TAB_INDEX_VOICEMAIL: + return mVoicemailFragment; + 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.hide(searchFragment); + ft.commitAllowingStateLoss(); + } + + private void prepareSearchView() { + final View searchViewLayout = getLayoutInflater().inflate( + R.layout.custom_action_bar, null); + mSearchView = (SearchView) searchViewLayout + .findViewById(R.id.search_view); + mSearchView.setOnQueryTextListener(mPhoneSearchQueryTextListener); + mSearchView.setOnCloseListener(mPhoneSearchCloseListener); + mSearchView.setQueryHint(getString(R.string.calllog_search_hint)); + mSearchView.setIconifiedByDefault(true); + mSearchView.setIconified(false); + + mSearchView + .setOnQueryTextFocusChangeListener(new OnFocusChangeListener() { + @Override + public void onFocusChange(View view, boolean hasFocus) { + if (hasFocus) { + showInputMethod(view.findFocus()); + } + } + }); + + getActionBar().setCustomView( + searchViewLayout, + new LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT)); + } + + private void showInputMethod(View view) { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + if (!imm.showSoftInput(view, 0)) { + } + } + } + + 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 OnQueryTextListener mPhoneSearchQueryTextListener = new OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + View view = getCurrentFocus(); + if (view != null) { + hideInputMethod(view); + view.clearFocus(); + } + return true; + } + + @Override + public boolean onQueryTextChange(String newText) { + // Show search result with non-empty text. Show a bare list + // otherwise. + if (mSearchFragment != null) { + mSearchFragment.setQueryString(newText); + } + return true; + } + }; + + /** + * Listener used to handle the "close" button on the right side of + * {@link SearchView}. If some text is in the search view, this will clean + * it up. Otherwise this will exit the search UI and let users go back to + * usual Phone UI. + * + * This does _not_ handle back button. + */ + private final OnCloseListener mPhoneSearchCloseListener = new OnCloseListener() { + @Override + public boolean onClose() { + if (!TextUtils.isEmpty(mSearchView.getQuery())) { + mSearchView.setQuery(null, true); + } + return true; + } + }; + + @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 = getActionBar(); + if (mSearchFragment != null) { + mSearchFragment.setUserVisibleHint(false); + + final FragmentTransaction transaction = getFragmentManager() + .beginTransaction(); + transaction.hide(mSearchFragment); + transaction.commitAllowingStateLoss(); + + } + + // We want to hide SearchView and show Tabs. Also focus on previously + // selected one. + actionBar.setDisplayShowCustomEnabled(false); + + for (int i = 0; i < mViewPagerAdapter.getCount(); i++) { + updateFragmentVisibility(i, i == mViewPager.getCurrentItem()); + } + + mViewPager.setVisibility(View.VISIBLE); + mViewPagerTabs.setVisibility(View.VISIBLE); + hideInputMethod(getCurrentFocus()); + + // Request to update option menu. + invalidateOptionsMenu(); + + // See comments in onActionViewExpanded() + mSearchView.onActionViewCollapsed(); + mSearchView.clearFocus(); + mInSearchUi = false; + } } diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java index 8e5fca7d7..589d80ec2 100644 --- a/src/com/android/dialer/calllog/CallLogAdapter.java +++ b/src/com/android/dialer/calllog/CallLogAdapter.java @@ -147,6 +147,8 @@ public class CallLogAdapter extends GroupingListAdapter private final OnReportButtonClickListener mOnReportButtonClickListener; private ViewTreeObserver mViewTreeObserver = null; + private String mFilterString; + /** * A cache of the contact details for the phone numbers in the call log. * <p> @@ -1415,4 +1417,8 @@ public class CallLogAdapter extends GroupingListAdapter } } } + + public void setQueryString(String filter) { + mFilterString = filter; + } } diff --git a/src/com/android/dialer/calllog/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java index b6f2e626a..77f83c1e9 100644 --- a/src/com/android/dialer/calllog/CallLogQueryHandler.java +++ b/src/com/android/dialer/calllog/CallLogQueryHandler.java @@ -138,6 +138,21 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler { fetchCalls(callType, 0); } + public void fetchCalls(String filter) { + cancelFetch(); + int requestId = newCallsRequest(); + fetchCalls(QUERY_CALLLOG_TOKEN ,requestId,filter); + } + + public void fetchCalls(int token,int requestId,String filter) { + String selection = "(" + Calls.NUMBER + " like '%" + filter + + "%' or " + Calls.CACHED_NAME + " like '%" + filter + "%' )"; + + startQuery(token, requestId, Calls.CONTENT_URI_WITH_VOICEMAIL, + CallLogQuery._PROJECTION, selection, null, + Calls.DEFAULT_SORT_ORDER); + } + public void fetchVoicemailStatus() { startQuery(QUERY_VOICEMAIL_STATUS_TOKEN, null, Status.CONTENT_URI, VoicemailStatusHelperImpl.PROJECTION, null, null, null); diff --git a/src/com/android/dialer/calllog/CallLogSearchFragment.java b/src/com/android/dialer/calllog/CallLogSearchFragment.java new file mode 100644 index 000000000..1f04539e7 --- /dev/null +++ b/src/com/android/dialer/calllog/CallLogSearchFragment.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2014, 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); + } + } + } + } + +} |