summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanny Baumann <dannybaumann@web.de>2015-05-29 09:22:03 +0200
committerDanny Baumann <dannybaumann@web.de>2015-06-10 06:56:41 +0000
commita3a0c7722ecc9162adc7575542bdce70f9d9c76a (patch)
tree265819568c006d61669582d3950cc4d9fddc78e1
parentb9bbfc413236c05feb2381be5c05a554d599b2ef (diff)
downloadandroid_packages_apps_Dialer-a3a0c7722ecc9162adc7575542bdce70f9d9c76a.tar.gz
android_packages_apps_Dialer-a3a0c7722ecc9162adc7575542bdce70f9d9c76a.tar.bz2
android_packages_apps_Dialer-a3a0c7722ecc9162adc7575542bdce70f9d9c76a.zip
Fix up call log search UX.
Change-Id: I0dc9556af8ae7328236a0efab0c96cdabc9938fc
-rw-r--r--AndroidManifest.xml2
-rw-r--r--res/layout/call_log_activity.xml29
-rw-r--r--res/menu/call_log_options.xml3
-rw-r--r--res/values/styles.xml16
-rw-r--r--src/com/android/dialer/PhoneCallDetailsHelper.java43
-rwxr-xr-xsrc/com/android/dialer/calllog/CallLogActivity.java616
-rwxr-xr-xsrc/com/android/dialer/calllog/CallLogAdapter.java2
-rw-r--r--src/com/android/dialer/calllog/CallLogListItemHelper.java5
-rw-r--r--src/com/android/dialer/calllog/MSimCallLogFragment.java14
-rw-r--r--src/com/android/dialer/callstats/CallStatsFragment.java16
-rw-r--r--tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java9
11 files changed, 470 insertions, 285 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a10b2c73f..580d567a3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -144,7 +144,7 @@
<activity android:name="com.android.dialer.calllog.CallLogActivity"
android:label="@string/call_log_activity_title"
- android:theme="@style/DialtactsThemeWithoutActionBarOverlay"
+ android:theme="@style/DialtactsThemeWithoutActionBar"
android:screenOrientation="nosensor"
android:icon="@mipmap/ic_launcher_phone">
<intent-filter>
diff --git a/res/layout/call_log_activity.xml b/res/layout/call_log_activity.xml
index 0109df7d6..67437db7f 100644
--- a/res/layout/call_log_activity.xml
+++ b/res/layout/call_log_activity.xml
@@ -19,15 +19,30 @@
android:layout_height="match_parent"
android:id="@+id/calllog_frame"
android:orientation="vertical">
- <com.android.contacts.common.list.ViewPagerTabs
- android:id="@+id/viewpager_header"
+
+ <LinearLayout
+ android:id="@+id/toolbar_container"
android:layout_width="match_parent"
- android:layout_height="@dimen/tab_height"
- android:textAllCaps="true"
- android:orientation="horizontal"
- android:layout_gravity="top"
+ android:layout_height="wrap_content"
android:elevation="@dimen/tab_elevation"
- style="@style/DialtactsActionBarTabTextStyle" />
+ android:orientation="vertical">
+
+ <Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:background="@color/actionbar_background_color" />
+ <com.android.contacts.common.list.ViewPagerTabs
+ android:id="@+id/viewpager_header"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/tab_height"
+ android:textAllCaps="true"
+ android:orientation="horizontal"
+ android:layout_gravity="top"
+ style="@style/DialtactsActionBarTabTextStyle" />
+
+ </LinearLayout>
+
<android.support.v4.view.ViewPager
android:id="@+id/call_log_pager"
android:layout_width="match_parent"
diff --git a/res/menu/call_log_options.xml b/res/menu/call_log_options.xml
index f0cbdf142..029c4da08 100644
--- a/res/menu/call_log_options.xml
+++ b/res/menu/call_log_options.xml
@@ -17,7 +17,8 @@
<item
android:id="@+id/search_calllog"
android:title="@string/calllog_search_hint"
- android:showAsAction="never"
+ android:icon="@drawable/ic_ab_search"
+ android:showAsAction="always"
android:orderInCategory="1"/>
<item
android:id="@+id/delete_all"
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 195f9cd70..c5dcfc96e 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -99,6 +99,12 @@
<item name="android:actionOverflowButtonStyle">@style/DialtactsActionBarOverflowWhite</item>
</style>
+ <style name="DialtactsThemeWithoutActionBar" parent="DialtactsThemeWithoutActionBarOverlay">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowActionBar">false</item>
+ <item name="android:toolbarStyle">@style/ToolbarStyle</item>
+ </style>
+
<!-- Hide the actionbar title during the activity preview -->
<style name="DialtactsActivityTheme" parent="DialtactsTheme">
<item name="android:actionBarStyle">@style/DialtactsActionBarWithoutTitleStyle</item>
@@ -106,12 +112,6 @@
<item name="android:fastScrollTrackDrawable">@null</item>
</style>
- <style name="DialtactsActionBarSpinner"
- parent="@*android:style/Widget.Holo.Light.Spinner.DropDown.ActionBar">
-
- <item name="android:background">@drawable/spinner_ab_holo_dark</item>
- </style>
-
<style name="CallDetailActivityTheme1" parent="android:Theme.Holo.Light">
<item name="android:windowBackground">@color/background_dialer_list_items</item>
<item name="android:gravity">top</item>
@@ -167,6 +167,10 @@
<item name="android:fontFamily">"sans-serif-medium"</item>
</style>
+ <style name="ToolbarStyle" parent="@android:style/Widget.Toolbar">
+ <item name="android:titleTextAppearance">@style/DialtactsActionBarTitleText</item>
+ </style>
+
<style name="ListViewStyle" parent="@android:style/Widget.Material.Light.ListView">
<item name="android:overScrollMode">always</item>
</style>
diff --git a/src/com/android/dialer/PhoneCallDetailsHelper.java b/src/com/android/dialer/PhoneCallDetailsHelper.java
index a63674ef6..6eac998cf 100644
--- a/src/com/android/dialer/PhoneCallDetailsHelper.java
+++ b/src/com/android/dialer/PhoneCallDetailsHelper.java
@@ -26,14 +26,13 @@ import android.telephony.SubscriptionManager;
import android.graphics.Typeface;
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;
import com.android.contacts.common.CallUtil;
+import com.android.contacts.common.format.TextHighlighter;
import com.android.contacts.common.testing.NeededForTesting;
import com.android.contacts.common.util.PhoneNumberHelper;
import com.android.dialer.calllog.ContactInfo;
@@ -60,6 +59,7 @@ public class PhoneCallDetailsHelper {
// Helper classes.
private final PhoneNumberDisplayHelper mPhoneNumberHelper;
private final PhoneNumberUtilsWrapper mPhoneNumberUtilsWrapper;
+ private final TextHighlighter mHighlighter;
/**
* List of items to be concatenated together for accessibility descriptions
@@ -79,6 +79,8 @@ public class PhoneCallDetailsHelper {
mResources = resources;
mPhoneNumberUtilsWrapper = phoneUtils;
mPhoneNumberHelper = new PhoneNumberDisplayHelper(context, resources, phoneUtils);
+ mHighlighter = new TextHighlighter(Typeface.BOLD,
+ mResources.getColor(R.color.text_highlight_color));
}
/** Fills the call details views with content. */
@@ -136,38 +138,25 @@ public class PhoneCallDetailsHelper {
views.callAccountLabel.setVisibility(View.GONE);
}
- CharSequence nameText;
- CharSequence displayNumber =
- mPhoneNumberHelper.getDisplayNumber(details.accountHandle, 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;
- }
+ final CharSequence nameText;
if (TextUtils.isEmpty(details.name)) {
- nameText = displayNumber;
+ nameText = mPhoneNumberHelper.getDisplayNumber(details.accountHandle,
+ details.number, details.numberPresentation, details.formattedNumber);
// 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);
+ int filterIndex = filter != null && nameText != null
+ ? nameText.toString().toLowerCase().indexOf(filter.toLowerCase()) : -1;
+ if (filterIndex >= 0) {
+ SpannableString s = new SpannableString(nameText);
+ mHighlighter.applyMaskingHighlight(s, filterIndex, filterIndex + filter.length());
+ views.nameView.setText(s);
+ } else {
+ views.nameView.setText(nameText);
+ }
if (isVoicemail && !TextUtils.isEmpty(details.transcription)) {
views.voicemailTranscriptionView.setText(details.transcription);
diff --git a/src/com/android/dialer/calllog/CallLogActivity.java b/src/com/android/dialer/calllog/CallLogActivity.java
index ae2d12e94..6f6dec474 100755
--- a/src/com/android/dialer/calllog/CallLogActivity.java
+++ b/src/com/android/dialer/calllog/CallLogActivity.java
@@ -15,107 +15,198 @@ m * limitations under the License.
*/
package com.android.dialer.calllog;
+import android.animation.ValueAnimator;
import android.app.ActionBar;
-import android.app.ActionBar.LayoutParams;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
-import android.app.ListFragment;
-import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Handler;
-import android.support.v13.app.FragmentPagerAdapter;
+import android.os.Parcelable;
+import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
+import android.text.Editable;
import android.text.TextUtils;
+import android.text.TextWatcher;
import android.view.View;
-import android.view.View.OnFocusChangeListener;
import android.view.inputmethod.InputMethodManager;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.ViewGroup;
+import android.widget.EditText;
import android.widget.SearchView;
-import android.widget.SearchView.OnCloseListener;
-import android.widget.SearchView.OnQueryTextListener;
+import android.widget.Toolbar;
import com.android.contacts.common.interactions.TouchPointManager;
import com.android.contacts.common.list.ViewPagerTabs;
+import com.android.contacts.common.util.ViewUtil;
import com.android.contacts.commonbind.analytics.AnalyticsUtil;
import com.android.dialer.DialtactsActivity;
import com.android.dialer.R;
import com.android.dialer.callstats.CallStatsFragment;
import com.android.dialer.widget.DoubleDatePickerDialog;
-import com.android.dialer.voicemail.VoicemailStatusHelper;
-import com.android.dialer.voicemail.VoicemailStatusHelperImpl;
-
public class CallLogActivity extends Activity implements
- ViewPager.OnPageChangeListener, DoubleDatePickerDialog.OnDateSetListener {
- private Handler mHandler;
+ ViewPager.OnPageChangeListener, SearchView.OnCloseListener,
+ View.OnClickListener, DoubleDatePickerDialog.OnDateSetListener {
private ViewPager mViewPager;
private ViewPagerTabs mViewPagerTabs;
- private FragmentPagerAdapter mViewPagerAdapter;
- private CallStatsFragment mStatsFragment;
+ private Toolbar mToolbar;
+ private MSimViewPagerAdapter mViewPagerAdapter;
- private MSimCallLogFragment mMSimCallsFragment;
+ private MSimCallLogFragment mCallsFragment;
+ private CallStatsFragment mStatsFragment;
private CallLogSearchFragment mSearchFragment;
- private SearchView mSearchView;
- private boolean mInSearchUi;
+
+ private int mCurrentTab;
+ private int mMaxTabHeight;
+ private int mMaxToolbarContentInsetStart;
+
+ private boolean mSearchMode;
+ private String mQueryString;
+
+ private EditText mSearchView;
+ private View mSearchContainer;
private static final int TAB_INDEX_MSIM = 0;
private static final int TAB_INDEX_MSIM_STATS = 1;
private static final int TAB_INDEX_COUNT_MSIM = 2;
- public class MSimViewPagerAdapter extends FragmentPagerAdapter {
+ private static final String EXTRA_KEY_SEARCH_MODE = "searchMode";
+ private static final String EXTRA_KEY_QUERY = "query";
+ private static final String EXTRA_KEY_SELECTED_TAB = "selectedTab";
+
+ public class MSimViewPagerAdapter extends PagerAdapter {
+ private final FragmentManager mFragmentManager;
+ private FragmentTransaction mCurTransaction = null;
+ private Fragment mCurrentPrimaryItem;
private String[] mTabTitles;
+ private boolean mPagerInSearchMode;
public MSimViewPagerAdapter(FragmentManager fm) {
- super(fm);
+ mFragmentManager = fm;
mTabTitles = new String[TAB_INDEX_COUNT_MSIM];
mTabTitles[0] = getString(R.string.call_log_all_title);
mTabTitles[1] = getString(R.string.call_log_stats_title);
}
+ public void setSearchMode(boolean searchMode) {
+ mPagerInSearchMode = searchMode;
+ notifyDataSetChanged();
+ }
+
+ public boolean isSearchMode() {
+ return mPagerInSearchMode;
+ }
+
@Override
- public Fragment getItem(int position) {
- switch (position) {
- case TAB_INDEX_MSIM:
- MSimCallLogFragment ms = new MSimCallLogFragment();
- ms.setHasOptionsMenu(true);
- return ms;
- case TAB_INDEX_MSIM_STATS:
- return new CallStatsFragment();
+ public int getCount() {
+ return mPagerInSearchMode ? 1 : TAB_INDEX_COUNT_MSIM;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return mTabTitles[position];
+ }
+
+ /** Gets called when the number of items changes. */
+ @Override
+ public int getItemPosition(Object object) {
+ if (mPagerInSearchMode) {
+ if (object == mSearchFragment) {
+ return 0;
+ }
+ } else {
+ if (object == mCallsFragment) {
+ return TAB_INDEX_MSIM;
+ }
+ if (object == mStatsFragment) {
+ return TAB_INDEX_MSIM_STATS;
+ }
}
- throw new IllegalStateException("No fragment at position " + position);
+ return POSITION_NONE;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
- final ListFragment fragment =
- (ListFragment) super.instantiateItem(container, position);
- switch (position) {
- case TAB_INDEX_MSIM:
- mMSimCallsFragment = (MSimCallLogFragment)fragment;
- break;
- case TAB_INDEX_MSIM_STATS:
- mStatsFragment = (CallStatsFragment)fragment;
- break;
+ if (mCurTransaction == null) {
+ mCurTransaction = mFragmentManager.beginTransaction();
}
- return fragment;
+ Fragment f = getFragment(position);
+ mCurTransaction.show(f);
+
+ // Non primary pages are not visible.
+ f.setUserVisibleHint(f == mCurrentPrimaryItem);
+ return f;
}
@Override
- public CharSequence getPageTitle(int position) {
- return mTabTitles[position];
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ if (mCurTransaction == null) {
+ mCurTransaction = mFragmentManager.beginTransaction();
+ }
+ mCurTransaction.hide((Fragment) object);
}
@Override
- public int getCount() {
- return TAB_INDEX_COUNT_MSIM;
+ public void startUpdate(ViewGroup container) {
+ }
+
+
+ @Override
+ public void finishUpdate(ViewGroup container) {
+ if (mCurTransaction != null) {
+ mCurTransaction.commitAllowingStateLoss();
+ mCurTransaction = null;
+ mFragmentManager.executePendingTransactions();
+ }
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return ((Fragment) object).getView() == view;
+ }
+
+ @Override
+ public void setPrimaryItem(ViewGroup container, int position, Object object) {
+ Fragment fragment = (Fragment) object;
+ if (mCurrentPrimaryItem != fragment) {
+ if (mCurrentPrimaryItem != null) {
+ mCurrentPrimaryItem.setUserVisibleHint(false);
+ }
+ if (fragment != null) {
+ fragment.setUserVisibleHint(true);
+ }
+ mCurrentPrimaryItem = fragment;
+ }
+ }
+
+ @Override
+ public Parcelable saveState() {
+ return null;
+ }
+
+ @Override
+ public void restoreState(Parcelable state, ClassLoader loader) {
+ }
+
+ private Fragment getFragment(int position) {
+ if (mPagerInSearchMode) {
+ return mSearchFragment;
+ }
+ switch (position) {
+ case TAB_INDEX_MSIM:
+ return mCallsFragment;
+ case TAB_INDEX_MSIM_STATS:
+ return mStatsFragment;
+ }
+ throw new IllegalStateException("No fragment at position " + position);
}
}
@@ -134,11 +225,16 @@ public class CallLogActivity extends Activity implements
setContentView(R.layout.call_log_activity);
getWindow().setBackgroundDrawable(null);
- final ActionBar actionBar = getActionBar();
- actionBar.setDisplayShowHomeEnabled(true);
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowTitleEnabled(true);
- actionBar.setElevation(0);
+ mToolbar = (Toolbar) findViewById(R.id.toolbar);
+ mMaxToolbarContentInsetStart = mToolbar.getContentInsetStart();
+
+ setActionBar(mToolbar);
+ setupSearchViews();
+
+ final View toolbarContainer = findViewById(R.id.toolbar_container);
+ ViewUtil.addRectangularOutlineProvider(toolbarContainer, getResources());
+
+ mMaxTabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height);
mViewPager = (ViewPager) findViewById(R.id.call_log_pager);
@@ -148,14 +244,23 @@ public class CallLogActivity extends Activity implements
mViewPagerTabs = (ViewPagerTabs) findViewById(R.id.viewpager_header);
mViewPager.setOnPageChangeListener(mViewPagerTabs);
mViewPagerTabs.setViewPager(mViewPager);
- addSearchFragment();
+
+ setupFragments();
+
+ if (savedInstanceState != null) {
+ setQueryString(savedInstanceState.getString(EXTRA_KEY_QUERY));
+ mSearchMode = savedInstanceState.getBoolean(EXTRA_KEY_SEARCH_MODE);
+ mCurrentTab = savedInstanceState.getInt(EXTRA_KEY_SELECTED_TAB);
+ }
+
+ update(true);
}
@Override
- public void onAttachFragment(Fragment fragment) {
- if (fragment instanceof CallLogSearchFragment) {
- mSearchFragment = (CallLogSearchFragment) fragment;
- }
+ protected void onSaveInstanceState(Bundle outState) {
+ outState.putBoolean(EXTRA_KEY_SEARCH_MODE, mSearchMode);
+ outState.putString(EXTRA_KEY_QUERY, mQueryString);
+ outState.putInt(EXTRA_KEY_SELECTED_TAB, mCurrentTab);
}
@Override
@@ -169,25 +274,15 @@ public class CallLogActivity extends Activity implements
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);
- }
+
+ if (mSearchMode) {
+ itemSearchCallLog.setVisible(false);
+ itemDeleteAll.setVisible(false);
} else {
- if (mSearchFragment != null && itemSearchCallLog != null) {
- final CallLogAdapter adapter = mSearchFragment.getAdapter();
- itemSearchCallLog.setVisible(adapter != null
- && !adapter.isEmpty());
- }
+ itemSearchCallLog.setVisible(true);
- // If onPrepareOptionsMenu is called before fragments loaded. Don't do anything.
- if (mMSimCallsFragment != null && itemDeleteAll != null) {
- final CallLogAdapter adapter = mMSimCallsFragment.getAdapter();
- itemDeleteAll.setVisible(adapter != null && !adapter.isEmpty());
- }
+ final CallLogAdapter adapter = mCallsFragment.getAdapter();
+ itemDeleteAll.setVisible(adapter != null && !adapter.isEmpty());
}
return true;
}
@@ -204,222 +299,286 @@ public class CallLogActivity extends Activity implements
onDelCallLog();
return true;
case R.id.search_calllog:
- enterSearchUi();
+ onSearchRequested();
return true;
}
return super.onOptionsItemSelected(item);
}
+ @Override
+ public boolean onSearchRequested() { // Search key pressed.
+ setSearchMode(true);
+ return true;
+ }
+
+ @Override
+ public boolean onClose() {
+ setSearchMode(false);
+ return false;
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.search_close_button:
+ setQueryString(null);
+ break;
+ case R.id.search_back_button:
+ onBackPressed();
+ break;
+ }
+ }
+
+ private void setupFragments() {
+ final String CALLS_TAG = "tab-pager-calls";
+ final String STATS_TAG = "tab-pager-stats";
+ final String SEARCH_TAG = "tab-pager-search";
+
+ final FragmentManager fm = getFragmentManager();
+ final FragmentTransaction transaction = fm.beginTransaction();
+
+ // Create the fragments and add as children of the view pager.
+ // The pager adapter will only change the visibility; it'll never create/destroy
+ // fragments.
+ // However, if it's after screen rotation, the fragments have been re-created by
+ // the fragment manager, so first see if there're already the target fragments
+ // existing.
+ mCallsFragment = (MSimCallLogFragment) fm.findFragmentByTag(CALLS_TAG);
+ mStatsFragment = (CallStatsFragment) fm.findFragmentByTag(STATS_TAG);
+ mSearchFragment = (CallLogSearchFragment) fm.findFragmentByTag(SEARCH_TAG);
+
+ if (mCallsFragment == null) {
+ mCallsFragment = new MSimCallLogFragment();
+ mStatsFragment = new CallStatsFragment();
+ mSearchFragment = new CallLogSearchFragment();
+
+ transaction.add(R.id.call_log_pager, mCallsFragment, CALLS_TAG);
+ transaction.add(R.id.call_log_pager, mStatsFragment, STATS_TAG);
+ transaction.add(R.id.call_log_pager, mSearchFragment, SEARCH_TAG);
+ }
+
+ transaction.hide(mCallsFragment);
+ transaction.hide(mStatsFragment);
+ transaction.hide(mSearchFragment);
+
+ transaction.commitAllowingStateLoss();
+ fm.executePendingTransactions();
+ }
+
+ private void setupSearchViews() {
+ final LayoutInflater inflater =
+ (LayoutInflater) mToolbar.getContext().getSystemService(LAYOUT_INFLATER_SERVICE);
+ mSearchContainer = inflater.inflate(R.layout.search_bar_expanded, mToolbar, false);
+ mSearchContainer.setVisibility(View.VISIBLE);
+ mSearchContainer.setBackgroundColor(
+ getResources().getColor(R.color.searchbox_background_color));
+
+ mSearchView = (EditText) mSearchContainer.findViewById(R.id.search_view);
+ mSearchView.setHint(getString(R.string.calllog_search_hint));
+ mSearchView.addTextChangedListener(new SearchTextWatcher());
+ mSearchContainer.findViewById(R.id.search_close_button).setOnClickListener(this);
+ mSearchContainer.findViewById(R.id.search_back_button).setOnClickListener(this);
+ }
+
private void onDelCallLog() {
Intent intent = new Intent(
"com.android.contacts.action.MULTI_PICK_CALL");
startActivity(intent);
}
- private void enterSearchUi() {
- if (mSearchFragment == null) {
- return;
- }
- if (mSearchView == null) {
- prepareSearchView();
- }
- final ActionBar actionBar = getActionBar();
-
- mSearchView.setQuery(null, true);
+ private void setFocusOnSearchView() {
mSearchView.requestFocus();
+ showInputMethod(mSearchView); // Workaround for the "IME not popping up" issue.
+ }
- actionBar.setDisplayShowCustomEnabled(true);
+ private void showInputMethod(View view) {
+ final InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
+ if (imm != null) {
+ imm.showSoftInput(view, 0);
+ }
+ }
- for (int i = 0; i < mViewPagerAdapter.getCount(); i++) {
- updateFragmentVisibility(i, false /* not visible */);
+ private void update(boolean skipAnimation) {
+ final boolean isIconifiedChanging = (mSearchContainer.getParent() == null) == mSearchMode;
+ if (!isIconifiedChanging) {
+ updateDisplayOptions(false);
+ return;
}
- 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;
+ if (skipAnimation) {
+ if (mSearchMode) {
+ setTabHeight(0);
+ addSearchContainer();
+ } else {
+ setTabHeight(mMaxTabHeight);
+ mToolbar.removeView(mSearchContainer);
+ }
+ updateDisplayOptions(true);
+ } else {
+ if (mSearchMode) {
+ addSearchContainer();
+ mSearchContainer.setAlpha(0);
+ mSearchContainer.animate().alpha(1);
+ animateTabHeightChange(mMaxTabHeight, 0);
+ updateDisplayOptions(true);
+ } else {
+ mSearchContainer.setAlpha(1);
+ animateTabHeightChange(0, mMaxTabHeight);
+ mSearchContainer.animate().alpha(0).withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ updateDisplayOptions(true);
+ mToolbar.removeView(mSearchContainer);
+ }
+ });
+ }
+ }
}
- private void updateFragmentVisibility(int position, boolean visibility) {
- if (position >= TAB_INDEX_MSIM) {
- final Fragment fragment = getFragmentAt(position);
- if (fragment != null) {
- fragment.setMenuVisibility(visibility);
- fragment.setUserVisibleHint(visibility);
+ private void updateDisplayOptions(boolean isIconifiedChanging) {
+ if (mSearchMode) {
+ setFocusOnSearchView();
+ // Since we have the {@link SearchView} in a custom action bar, we must manually handle
+ // expanding the {@link SearchView} when a search is initiated. Note that a side effect
+ // of this method is that the {@link SearchView} query text is set to empty string.
+ if (isIconifiedChanging) {
+ final CharSequence queryText = mSearchView.getText();
+ if (!TextUtils.isEmpty(queryText)) {
+ mSearchView.setText(queryText);
+ }
}
+ } else if (mViewPager.getCurrentItem() != mCurrentTab) {
+ mViewPager.setCurrentItem(mCurrentTab, !mViewPagerAdapter.isSearchMode());
}
+
+ updateDisplayOptionsInner();
+ mViewPagerAdapter.setSearchMode(mSearchMode);
}
- private Fragment getFragmentAt(int position) {
- switch (position) {
- case TAB_INDEX_MSIM:
- return mMSimCallsFragment;
- case TAB_INDEX_MSIM_STATS:
- return mStatsFragment;
- default:
- throw new IllegalStateException("Unknown fragment index: "
- + position);
+ private void updateDisplayOptionsInner() {
+ // All the flags we may change in this method.
+ final int MASK = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME
+ | ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_CUSTOM;
+
+ // The current flags set to the action bar. (only the ones that we may change here)
+ final ActionBar actionBar = getActionBar();
+ final int current = actionBar.getDisplayOptions() & MASK;
+
+ // Build the new flags...
+ int newFlags = 0;
+ if (mSearchMode) {
+ newFlags |= ActionBar.DISPLAY_SHOW_CUSTOM;
+ mToolbar.setContentInsetsRelative(0, mToolbar.getContentInsetEnd());
+ } else {
+ newFlags |= ActionBar.DISPLAY_SHOW_TITLE;
+ newFlags |= ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP;
+ mToolbar.setContentInsetsRelative(mMaxToolbarContentInsetStart,
+ mToolbar.getContentInsetEnd());
}
+
+
+ if (current != newFlags) {
+ // Pass the mask here to preserve other flags that we're not interested here.
+ actionBar.setDisplayOptions(newFlags, MASK);
+ }
+ }
+
+ private void addSearchContainer() {
+ mToolbar.removeView(mSearchContainer);
+ mToolbar.addView(mSearchContainer);
}
- private void addSearchFragment() {
- if (mSearchFragment != null) {
+ private void animateTabHeightChange(int start, int end) {
+ if (mViewPagerTabs == 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();
+ final ValueAnimator animator = ValueAnimator.ofInt(start, end);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ int value = (Integer) valueAnimator.getAnimatedValue();
+ setTabHeight(value);
+ }
+ });
+ animator.setDuration(100).start();
}
- 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());
- }
- }
- });
+ private void setTabHeight(int height) {
+ if (mViewPagerTabs == null) {
+ return;
+ }
+ ViewGroup.LayoutParams layoutParams = mViewPagerTabs.getLayoutParams();
+ layoutParams.height = height;
+ mViewPagerTabs.setLayoutParams(layoutParams);
+ }
- getActionBar().setCustomView(
- searchViewLayout,
- new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.WRAP_CONTENT));
+ private void setQueryString(String query) {
+ mQueryString = query;
+ if (mSearchView != null) {
+ mSearchView.setText(query);
+ // When programmatically entering text into the search view, the most reasonable
+ // place for the cursor is after all the text.
+ mSearchView.setSelection(mSearchView.getText() == null ?
+ 0 : mSearchView.getText().length());
+ }
}
- private void showInputMethod(View view) {
- InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- if (imm != null) {
- if (!imm.showSoftInput(view, 0)) {
+ private void setSearchMode(boolean flag) {
+ if (mSearchMode == flag) {
+ if (flag) {
+ setFocusOnSearchView();
}
+ return;
}
- }
- private void hideInputMethod(View view) {
- InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- if (imm != null && view != null) {
- imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+ mSearchMode = flag;
+ update(false);
+ if (flag) {
+ mSearchView.setEnabled(true);
+ setFocusOnSearchView();
+ } else {
+ mSearchView.setEnabled(false);
}
+
+ setQueryString(null);
}
- /**
- * Listener used to send search queries to the phone search fragment.
- */
- private final OnQueryTextListener mPhoneSearchQueryTextListener = new OnQueryTextListener() {
+ private class SearchTextWatcher implements TextWatcher {
@Override
- public boolean onQueryTextSubmit(String query) {
- View view = getCurrentFocus();
- if (view != null) {
- hideInputMethod(view);
- view.clearFocus();
+ public void onTextChanged(CharSequence queryString, int start, int before, int count) {
+ if (queryString.equals(mQueryString)) {
+ return;
+ }
+ mQueryString = queryString.toString();
+ mSearchFragment.setQueryString(mQueryString);
+ if (!mSearchMode && !TextUtils.isEmpty(queryString)) {
+ setSearchMode(true);
}
- 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;
- }
- };
+ public void afterTextChanged(Editable s) {}
- /**
- * 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;
- }
- };
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+ }
@Override
public void onBackPressed() {
- if (mInSearchUi) {
+ if (mSearchMode) {
// We should let the user go back to usual screens with tabs.
- exitSearchUi();
+ setSearchMode(false);
} 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;
- }
-
@Override
public void onDateSet(long from, long to) {
switch (mViewPager.getCurrentItem()) {
case TAB_INDEX_MSIM:
- mMSimCallsFragment.onDateSet(from, to);
+ mCallsFragment.onDateSet(from, to);
break;
case TAB_INDEX_MSIM_STATS:
mStatsFragment.onDateSet(from, to);
@@ -438,6 +597,9 @@ public class CallLogActivity extends Activity implements
sendScreenViewForChildFragment(position);
}
mViewPagerTabs.onPageSelected(position);
+ if (!mSearchMode) {
+ mCurrentTab = position;
+ }
}
@Override
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index 7f8a33db2..05fd69855 100755
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -467,7 +467,7 @@ public class CallLogAdapter extends GroupingListAdapter
sourceType, accountHandle, features, dataUsage, transcription);
}
- mCallLogViewsHelper.setPhoneCallDetails(mContext, views, details);
+ mCallLogViewsHelper.setPhoneCallDetails(mContext, views, details, mFilterString);
int contactType = ContactPhotoManager.TYPE_DEFAULT;
diff --git a/src/com/android/dialer/calllog/CallLogListItemHelper.java b/src/com/android/dialer/calllog/CallLogListItemHelper.java
index 77ad333d9..b0e0e5a8f 100644
--- a/src/com/android/dialer/calllog/CallLogListItemHelper.java
+++ b/src/com/android/dialer/calllog/CallLogListItemHelper.java
@@ -60,10 +60,11 @@ import com.android.dialer.R;
* @param context The application context.
* @param views the views to populate
* @param details the details of a phone call needed to fill in the data
+ * @param filter A filter string to highlight (or null if there's no filter active)
*/
public void setPhoneCallDetails(
- Context context, CallLogListItemViews views, PhoneCallDetails details) {
- mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details);
+ Context context, CallLogListItemViews views, 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/MSimCallLogFragment.java b/src/com/android/dialer/calllog/MSimCallLogFragment.java
index 193059a4c..4676678c8 100644
--- a/src/com/android/dialer/calllog/MSimCallLogFragment.java
+++ b/src/com/android/dialer/calllog/MSimCallLogFragment.java
@@ -148,6 +148,12 @@ public class MSimCallLogFragment extends CallLogFragment
}
@Override
+ public void onActivityCreated(Bundle savedState) {
+ setHasOptionsMenu(true);
+ super.onActivityCreated(savedState);
+ }
+
+ @Override
public void fetchCalls() {
fetchCalls(mFilterFrom, mFilterTo, mCallSlotFilter);
}
@@ -220,9 +226,11 @@ public class MSimCallLogFragment extends CallLogFragment
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- inflater.inflate(R.menu.call_log_fragment_options, menu);
- MenuItem resetItem = menu.findItem(R.id.reset_date_filter);
- resetItem.setVisible(mFilterFrom != -1);
+ if (getUserVisibleHint()) {
+ inflater.inflate(R.menu.call_log_fragment_options, menu);
+ MenuItem resetItem = menu.findItem(R.id.reset_date_filter);
+ resetItem.setVisible(mFilterFrom != -1);
+ }
}
@Override
diff --git a/src/com/android/dialer/callstats/CallStatsFragment.java b/src/com/android/dialer/callstats/CallStatsFragment.java
index 89a8504a1..fcfcbdafd 100644
--- a/src/com/android/dialer/callstats/CallStatsFragment.java
+++ b/src/com/android/dialer/callstats/CallStatsFragment.java
@@ -156,15 +156,17 @@ public class CallStatsFragment extends ListFragment implements
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- inflater.inflate(R.menu.call_stats_options, menu);
+ if (getUserVisibleHint()) {
+ inflater.inflate(R.menu.call_stats_options, menu);
- final MenuItem resetItem = menu.findItem(R.id.reset_date_filter);
- final MenuItem sortDurationItem = menu.findItem(R.id.sort_by_duration);
- final MenuItem sortCountItem = menu.findItem(R.id.sort_by_count);
+ final MenuItem resetItem = menu.findItem(R.id.reset_date_filter);
+ final MenuItem sortDurationItem = menu.findItem(R.id.sort_by_duration);
+ final MenuItem sortCountItem = menu.findItem(R.id.sort_by_count);
- resetItem.setVisible(mFilterFrom != -1);
- sortDurationItem.setVisible(!mSortByDuration);
- sortCountItem.setVisible(mSortByDuration);
+ resetItem.setVisible(mFilterFrom != -1);
+ sortDurationItem.setVisible(!mSortByDuration);
+ sortCountItem.setVisible(mSortByDuration);
+ }
super.onCreateOptionsMenu(menu, inflater);
}
diff --git a/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java b/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
index 30a84c2f3..b14eed166 100644
--- a/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
+++ b/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
@@ -353,7 +353,8 @@ public class CallLogListItemHelperTest extends AndroidTestCase {
mHelper.setPhoneCallDetails(getContext(), mViews,
new PhoneCallDetails(number, presentation, formattedNumber,
TEST_COUNTRY_ISO, TEST_GEOCODE,
- new int[]{ callType }, TEST_DATE, TEST_DURATION)
+ new int[]{ callType }, TEST_DATE, TEST_DURATION),
+ null
);
}
@@ -362,7 +363,8 @@ public class CallLogListItemHelperTest extends AndroidTestCase {
mHelper.setPhoneCallDetails(getContext() ,mViews,
new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
TEST_FORMATTED_NUMBER, TEST_COUNTRY_ISO, TEST_GEOCODE,
- types, TEST_DATE, TEST_DURATION)
+ types, TEST_DATE, TEST_DURATION),
+ null
);
}
@@ -371,7 +373,8 @@ public class CallLogListItemHelperTest extends AndroidTestCase {
mHelper.setPhoneCallDetails(getContext(), mViews,
new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
TEST_FORMATTED_NUMBER, TEST_COUNTRY_ISO, TEST_GEOCODE,
- types, TEST_DATE, TEST_DURATION)
+ types, TEST_DATE, TEST_DURATION),
+ null
);
}
}