import android.net.Uri;
import android.net.Uri.Builder;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
import android.os.Parcelable;
import android.preference.PreferenceManager;
import android.provider.ContactsContract;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewParent;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View.OnFocusChangeListener;
+import android.view.View.OnTouchListener;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Filter;
-import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.QuickContactBadge;
import android.widget.ResourceCursorAdapter;
import android.widget.SectionIndexer;
-import android.widget.TabHost;
import android.widget.TextView;
import android.widget.AbsListView.OnScrollListener;
-import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Random;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
/**
* Displays a list of contacts. Usually is embedded into the ContactsActivity.
*/
@SuppressWarnings("deprecation")
public class ContactsListActivity extends ListActivity implements View.OnCreateContextMenuListener,
- View.OnClickListener, View.OnKeyListener, TextWatcher, TextView.OnEditorActionListener {
+ View.OnClickListener, View.OnKeyListener, TextWatcher, TextView.OnEditorActionListener,
+ OnFocusChangeListener, OnTouchListener {
public static class JoinContactActivity extends ContactsListActivity {
}
+ public static class ContactsSearchActivity extends ContactsListActivity {
+
+ }
+
private static final String TAG = "ContactsListActivity";
private static final boolean ENABLE_ACTION_ICON_OVERLAYS = true;
private static final String LIST_STATE_KEY = "liststate";
-
- /**
- * Saved state key for the flag that indicates if the UI is in the search mode.
- */
- private static final String SEARCH_MODE_KEY = "searchMode";
+ private static final String SHORTCUT_ACTION_KEY = "shortcutAction";
static final int MENU_ITEM_VIEW_CONTACT = 1;
static final int MENU_ITEM_CALL = 2;
private static final int SUBACTIVITY_VIEW_CONTACT = 2;
private static final int SUBACTIVITY_DISPLAY_GROUP = 3;
private static final int SUBACTIVITY_SEARCH = 4;
+ private static final int SUBACTIVITY_FILTER = 5;
private static final int TEXT_HIGHLIGHTING_ANIMATION_DURATION = 350;
/** Show all contacts and pick them when clicking, and allow creating a new contact */
static final int MODE_INSERT_OR_EDIT_CONTACT = 45 | MODE_MASK_PICKER | MODE_MASK_CREATE_NEW;
/** Show all phone numbers and pick them when clicking */
- // TODO fix and reenable search in phone number picker
- static final int MODE_PICK_PHONE = 50 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE |
- MODE_MASK_NO_FILTER;
+ static final int MODE_PICK_PHONE = 50 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE;
/** Show all phone numbers through the legacy provider and pick them when clicking */
static final int MODE_LEGACY_PICK_PHONE =
51 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE | MODE_MASK_NO_FILTER;
/** Show all postal addresses and pick them when clicking */
static final int MODE_PICK_POSTAL =
- 55 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE | MODE_MASK_NO_FILTER;
+ 55 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE;
/** Show all postal addresses and pick them when clicking */
static final int MODE_LEGACY_PICK_POSTAL =
56 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE | MODE_MASK_NO_FILTER;
static final int MODE_GROUP = 57 | MODE_MASK_SHOW_PHOTOS;
/** Run a search query */
- static final int MODE_QUERY = 60 | MODE_MASK_NO_FILTER | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
+ static final int MODE_QUERY = 60 | MODE_MASK_SHOW_PHOTOS | MODE_MASK_NO_FILTER
+ | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
/** Run a search query in PICK mode, but that still launches to VIEW */
- static final int MODE_QUERY_PICK_TO_VIEW = 65 | MODE_MASK_NO_FILTER | MODE_MASK_PICKER;
+ static final int MODE_QUERY_PICK_TO_VIEW = 65 | MODE_MASK_SHOW_PHOTOS | MODE_MASK_PICKER
+ | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
/** Show join suggestions followed by an A-Z list */
static final int MODE_JOIN_CONTACT = 70 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE
| MODE_MASK_NO_DATA | MODE_MASK_SHOW_PHOTOS | MODE_MASK_DISABLE_QUIKCCONTACT;
/** Run a search query in a PICK mode */
- static final int MODE_QUERY_PICK = 75 | MODE_MASK_NO_FILTER | MODE_MASK_PICKER;
+ static final int MODE_QUERY_PICK = 75 | MODE_MASK_SHOW_PHOTOS | MODE_MASK_NO_FILTER
+ | MODE_MASK_PICKER | MODE_MASK_DISABLE_QUIKCCONTACT | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
+
+ /** Run a search query in a PICK_PHONE mode */
+ static final int MODE_QUERY_PICK_PHONE = 80 | MODE_MASK_NO_FILTER | MODE_MASK_PICKER
+ | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
/**
* An action used to do perform search while in a contact picker. It is initiated
* by the ContactListActivity itself.
*/
- private static final String ACTION_INTERNAL_SEARCH = "com.android.contacts.INTERNAL_SEARCH";
+ private static final String ACTION_SEARCH_FOR_SHORTCUT = "com.android.contacts.INTERNAL_SEARCH";
/** Maximum number of suggestions shown for joining aggregates */
static final int MAX_SUGGESTIONS = 4;
private String mShortcutAction;
- private int mScrollState;
-
/**
* Internal query type when in mode {@link #MODE_QUERY_PICK_TO_VIEW}.
*/
private static final int QUERY_MODE_MAILTO = 1;
private static final int QUERY_MODE_TEL = 2;
- /**
- * Data to use when in mode {@link #MODE_QUERY_PICK_TO_VIEW}. Usually
- * provided by scheme-specific part of incoming {@link Intent#getData()}.
- */
- private String mQueryData;
+ private boolean mSearchMode;
+ private boolean mShowNumberOfContacts;
+
+ private String mInitialFilter;
private static final String CLAUSE_ONLY_VISIBLE = Contacts.IN_VISIBLE_GROUP + "=1";
private static final String CLAUSE_ONLY_PHONES = Contacts.HAS_PHONE_NUMBER + "=1";
private static final UriMatcher sContactsIdMatcher;
private ContactPhotoLoader mPhotoLoader;
- private static ExecutorService sImageFetchThreadPool;
static {
sContactsIdMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private int mSortOrder;
private boolean mHighlightWhenScrolling;
private TextHighlightingAnimation mHighlightingAnimation;
-
- // If true, the activity is in the "search mode" with the search UI displayed.
- private boolean mSearchMode;
- private View mSearchView;
private SearchEditText mSearchEditText;
/**
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
- // Resolve the intent
- final Intent intent = getIntent();
-
mIconSize = getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
mContactsPrefs = new ContactsPreferences(this);
mPhotoLoader = new ContactPhotoLoader(this, R.drawable.ic_contact_list_picture);
+ // Resolve the intent
+ final Intent intent = getIntent();
+
// Allow the title to be set to a custom String using an extra on the intent
String title = intent.getStringExtra(UI.TITLE_EXTRA_KEY);
if (title != null) {
setTitle(title);
}
- final String action = intent.getAction();
- mMode = MODE_UNKNOWN;
+ String action = intent.getAction();
+ String component = intent.getComponent().getClassName();
+
+ // When we get a FILTER_CONTACTS_ACTION, it represents search in the context
+ // of some other action. Let's retrieve the original action to provide proper
+ // context for the search queries.
+ if (UI.FILTER_CONTACTS_ACTION.equals(action)) {
+ mSearchMode = true;
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ mInitialFilter = extras.getString(UI.FILTER_TEXT_EXTRA_KEY);
+ String originalAction =
+ extras.getString(ContactsSearchManager.ORIGINAL_ACTION_EXTRA_KEY);
+ if (originalAction != null) {
+ action = originalAction;
+ }
+ String originalComponent =
+ extras.getString(ContactsSearchManager.ORIGINAL_COMPONENT_EXTRA_KEY);
+ if (originalComponent != null) {
+ component = originalComponent;
+ }
+ } else {
+ mInitialFilter = null;
+ }
+ }
Log.i(TAG, "Called with action: " + action);
- if (UI.LIST_DEFAULT.equals(action)) {
+ mMode = MODE_UNKNOWN;
+ if (UI.LIST_DEFAULT.equals(action) || UI.FILTER_CONTACTS_ACTION.equals(action)) {
mMode = MODE_DEFAULT;
// When mDefaultMode is true the mode is set in onResume(), since the preferneces
// activity may change it whenever this activity isn't running
mMode = MODE_CUSTOM;
mDisplayOnlyPhones = false;
} else if (UI.LIST_STARRED_ACTION.equals(action)) {
- mMode = MODE_STARRED;
+ mMode = mSearchMode ? MODE_DEFAULT : MODE_STARRED;
} else if (UI.LIST_FREQUENT_ACTION.equals(action)) {
- mMode = MODE_FREQUENT;
+ mMode = mSearchMode ? MODE_DEFAULT : MODE_FREQUENT;
} else if (UI.LIST_STREQUENT_ACTION.equals(action)) {
- mMode = MODE_STREQUENT;
+ mMode = mSearchMode ? MODE_DEFAULT : MODE_STREQUENT;
} else if (UI.LIST_CONTACTS_WITH_PHONES_ACTION.equals(action)) {
mMode = MODE_CUSTOM;
mDisplayOnlyPhones = true;
mMode = MODE_LEGACY_PICK_POSTAL;
}
} else if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
- if (intent.getComponent().getClassName().equals("alias.DialShortcut")) {
+ if (component.equals("alias.DialShortcut")) {
mMode = MODE_PICK_PHONE;
mShortcutAction = Intent.ACTION_CALL;
setTitle(R.string.callShortcutActivityTitle);
- } else if (intent.getComponent().getClassName().equals("alias.MessageShortcut")) {
+ } else if (component.equals("alias.MessageShortcut")) {
mMode = MODE_PICK_PHONE;
mShortcutAction = Intent.ACTION_SENDTO;
setTitle(R.string.messageShortcutActivityTitle);
+ } else if (mSearchMode) {
+ mMode = MODE_PICK_CONTACT;
+ mShortcutAction = Intent.ACTION_VIEW;
+ setTitle(R.string.shortcutActivityTitle);
} else {
mMode = MODE_PICK_OR_CREATE_CONTACT;
mShortcutAction = Intent.ACTION_VIEW;
} else if (Intent.ACTION_GET_CONTENT.equals(action)) {
final String type = intent.resolveType(this);
if (Contacts.CONTENT_ITEM_TYPE.equals(type)) {
- mMode = MODE_PICK_OR_CREATE_CONTACT;
+ if (mSearchMode) {
+ mMode = MODE_PICK_CONTACT;
+ } else {
+ mMode = MODE_PICK_OR_CREATE_CONTACT;
+ }
} else if (Phone.CONTENT_ITEM_TYPE.equals(type)) {
mMode = MODE_PICK_PHONE;
} else if (Phones.CONTENT_ITEM_TYPE.equals(type)) {
} else if (ContactMethods.CONTENT_POSTAL_ITEM_TYPE.equals(type)) {
mMode = MODE_LEGACY_PICK_POSTAL;
} else if (People.CONTENT_ITEM_TYPE.equals(type)) {
- mMode = MODE_LEGACY_PICK_OR_CREATE_PERSON;
+ if (mSearchMode) {
+ mMode = MODE_LEGACY_PICK_PERSON;
+ } else {
+ mMode = MODE_LEGACY_PICK_OR_CREATE_PERSON;
+ }
}
} else if (Intent.ACTION_INSERT_OR_EDIT.equals(action)) {
if (intent.hasExtra(Insert.EMAIL)) {
mMode = MODE_QUERY_PICK_TO_VIEW;
mQueryMode = QUERY_MODE_MAILTO;
- mQueryData = intent.getStringExtra(Insert.EMAIL);
+ mInitialFilter = intent.getStringExtra(Insert.EMAIL);
} else if (intent.hasExtra(Insert.PHONE)) {
mMode = MODE_QUERY_PICK_TO_VIEW;
mQueryMode = QUERY_MODE_TEL;
- mQueryData = intent.getStringExtra(Insert.PHONE);
+ mInitialFilter = intent.getStringExtra(Insert.PHONE);
} else {
// Otherwise handle the more normal search case
mMode = MODE_QUERY;
- mQueryData = getIntent().getStringExtra(SearchManager.QUERY);
+ mInitialFilter = getIntent().getStringExtra(SearchManager.QUERY);
+ }
+ } else if (ACTION_SEARCH_FOR_SHORTCUT.equals(action)) {
+ // The search request has extras to specify query
+ mShortcutAction = intent.getStringExtra(SHORTCUT_ACTION_KEY);
+ if (intent.hasExtra(Insert.PHONE)) {
+ mMode = MODE_QUERY_PICK_PHONE;
+ mQueryMode = QUERY_MODE_TEL;
+ mInitialFilter = intent.getStringExtra(Insert.PHONE);
+ } else {
+ mMode = MODE_QUERY_PICK;
+ mQueryMode = QUERY_MODE_NONE;
+ mInitialFilter = getIntent().getStringExtra(SearchManager.QUERY);
}
- } else if (ACTION_INTERNAL_SEARCH.equals(action)) {
- mMode = MODE_QUERY_PICK;
- mQueryData = getIntent().getStringExtra(SearchManager.QUERY);
// Since this is the filter activity it receives all intents
// dispatched from the SearchManager for security reasons
}
if (JOIN_AGGREGATE.equals(action)) {
- mMode = MODE_JOIN_CONTACT;
- mQueryAggregateId = intent.getLongExtra(EXTRA_AGGREGATE_ID, -1);
- if (mQueryAggregateId == -1) {
- Log.e(TAG, "Intent " + action + " is missing required extra: "
- + EXTRA_AGGREGATE_ID);
- setResult(RESULT_CANCELED);
- finish();
+ if (mSearchMode) {
+ mMode = MODE_PICK_CONTACT;
+ } else {
+ mMode = MODE_JOIN_CONTACT;
+ mQueryAggregateId = intent.getLongExtra(EXTRA_AGGREGATE_ID, -1);
+ if (mQueryAggregateId == -1) {
+ Log.e(TAG, "Intent " + action + " is missing required extra: "
+ + EXTRA_AGGREGATE_ID);
+ setResult(RESULT_CANCELED);
+ finish();
+ }
}
}
mMode = MODE_DEFAULT;
}
+ if ((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0 || mSearchMode) {
+ mShowNumberOfContacts = true;
+ }
+
if (mMode == MODE_JOIN_CONTACT) {
setContentView(R.layout.contacts_list_content_join);
TextView blurbView = (TextView)findViewById(R.id.join_contact_blurb);
getContactDisplayName(mQueryAggregateId));
blurbView.setText(blurb);
mJoinModeShowAllContacts = true;
+ } else if (mSearchMode) {
+ setContentView(R.layout.contacts_search_content);
} else {
setContentView(R.layout.contacts_list_content);
}
setupListView();
- setupSearchView();
+ if (mSearchMode) {
+ setupSearchView();
+ }
mQueryHandler = new QueryHandler(this);
mJustCreated = true;
// Tell list view to not show dividers. We'll do it ourself so that we can *not* show
// them when an A-Z headers is visible.
list.setDividerHeight(0);
- list.setFocusable(true);
+ list.setFocusable((mMode & MODE_MASK_NO_FILTER) == 0);
list.setOnCreateContextMenuListener(this);
- // Set the proper empty string
- setEmptyText();
-
mAdapter = new ContactItemListAdapter(this);
setListAdapter(mAdapter);
list.setOnScrollListener(mAdapter);
list.setOnKeyListener(this);
+ list.setOnFocusChangeListener(this);
+ list.setOnTouchListener(this);
// We manually save/restore the listview state
list.setSaveEnabled(false);
* Configures search UI.
*/
private void setupSearchView() {
- if ((mMode & MODE_MASK_NO_FILTER) == 0) {
- mSearchView = findViewById(R.id.searchView);
- mSearchEditText = (SearchEditText)mSearchView.findViewById(R.id.search_src_text);
- mSearchEditText.addTextChangedListener(this);
- mSearchEditText.setOnEditorActionListener(this);
-
- ImageButton searchButton = (ImageButton)mSearchView.findViewById(R.id.search_btn);
- searchButton.setOnClickListener(this);
- }
- }
-
- private boolean isPickerMode() {
- return mMode == MODE_PICK_CONTACT
- || mMode == MODE_PICK_OR_CREATE_CONTACT
- || mMode == MODE_LEGACY_PICK_PERSON
- || mMode == MODE_LEGACY_PICK_OR_CREATE_PERSON
- || mMode == MODE_QUERY_PICK;
+ mSearchEditText = (SearchEditText)findViewById(R.id.search_src_text);
+ mSearchEditText.addTextChangedListener(this);
+ mSearchEditText.setOnEditorActionListener(this);
+ mSearchEditText.setText(mInitialFilter);
}
private String getContactDisplayName(long contactId) {
return contactName;
}
-
private int getSummaryDisplayNameColumnIndex() {
if (mDisplayOrder == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
return SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX;
}
break;
}
- case R.id.search_btn: {
- doSearch();
- break;
- }
}
}
private void setEmptyText() {
- if (mMode == MODE_JOIN_CONTACT) {
+ if (mMode == MODE_JOIN_CONTACT || mSearchMode) {
return;
}
TextView empty = (TextView) findViewById(R.id.emptyText);
-
- if (mSearchMode) {
- empty.setText(getText(R.string.noMatchingFilteredContacts));
- } else if (mDisplayOnlyPhones) {
+ if (mDisplayOnlyPhones) {
empty.setText(getText(R.string.noContactsWithPhoneNumbers));
} else if (mMode == MODE_STREQUENT || mMode == MODE_STARRED) {
empty.setText(getText(R.string.noFavoritesHelpText));
mDisplayOnlyPhones = prefs.getBoolean(Prefs.DISPLAY_ONLY_PHONES,
Prefs.DISPLAY_ONLY_PHONES_DEFAULT);
-
- // Update the empty text view with the proper string, as the group may have changed
- setEmptyText();
}
@Override
super.onResume();
mPhotoLoader.resume();
- mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
- boolean runQuery = true;
Activity parent = getParent();
// Do this before setting the filter. The filter thread relies
setDefaultMode();
}
+ // See if we were invoked with a filter
if (mSearchMode) {
- startSearchMode(false);
+ mSearchEditText.requestFocus();
}
- if (mJustCreated && runQuery) {
+ if (mJustCreated) {
// We need to start a query here the first time the activity is launched, as long
// as we aren't doing a filter.
startQuery();
return null;
}
- private void setTextFilter(String filterText) {
- if (mSearchEditText != null) {
- mSearchEditText.setText(filterText);
- }
- }
-
@Override
protected void onRestart() {
super.onRestart();
protected void onSaveInstanceState(Bundle icicle) {
super.onSaveInstanceState(icicle);
// Save list state in the bundle so we can restore it after the QueryHandler has run
- icicle.putParcelable(LIST_STATE_KEY, mList.onSaveInstanceState());
- icicle.putBoolean(SEARCH_MODE_KEY, mSearchMode);
+ if (mList != null) {
+ icicle.putParcelable(LIST_STATE_KEY, mList.onSaveInstanceState());
+ }
}
@Override
super.onRestoreInstanceState(icicle);
// Retrieve list state. This will be applied after the QueryHandler has run
mListState = icicle.getParcelable(LIST_STATE_KEY);
- mSearchMode = icicle.getBoolean(SEARCH_MODE_KEY);
}
@Override
return true;
}
case R.id.menu_search: {
- startSearchMode(true);
+ onSearchRequested();
return true;
}
case R.id.menu_add: {
return false;
}
- /**
- * Displays and initializes the search UI at the top of the activity. If
- * this activity is part of a tab activity, also removes the tabs.
- *
- * @param showKeyboard a flag indicating whether the soft keyboard should be
- * auto shown automatically.
- */
- private void startSearchMode(boolean showKeyboard) {
- View tabs = findTabWidget();
- if (tabs != null) {
- tabs.setVisibility(View.GONE);
- }
-
- mList.setFocusable(false);
- mSearchEditText.setAutoShowKeyboard(showKeyboard);
- mSearchEditText.requestFocus();
- mSearchView.setVisibility(View.VISIBLE);
- mSearchMode = true;
- setEmptyText();
- }
-
- /**
- * Hides the search UI and shows the tabs if they were hidden before.
- */
- private void stopSearchMode() {
-
- // In case the list view owns the soft keyboard at this point, hide the keyboard
- InputMethodManager inputManager = (InputMethodManager)getSystemService(
- Context.INPUT_METHOD_SERVICE);
- inputManager.hideSoftInputFromWindow(mList.getWindowToken(), 0);
-
- // In case the search text view owns the soft keyboard, do the same
- mSearchEditText.hideKeyboard();
- mSearchView.setVisibility(View.GONE);
-
- View tabs = findTabWidget();
- if (tabs != null) {
- tabs.setVisibility(View.VISIBLE);
- }
-
- mSearchMode = false;
- setEmptyText();
-
- // This will trigger a query
- setTextFilter(null);
-
- mList.setFocusable(true);
- }
-
- /**
- * If this activity is hosted by a tab activity, the method returns the
- * TabWidget from the TabHost activity; otherwise it returns null.
- */
- private View findTabWidget() {
- View start = getListView();
- ViewParent parent = start.getParent();
- while (parent != null) {
- if (parent instanceof TabHost) {
- return ((TabHost)parent).getTabWidget();
+ @Override
+ public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+ boolean globalSearch) {
+ if (globalSearch) {
+ super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+ } else {
+ if (!mSearchMode && (mMode & MODE_MASK_NO_FILTER) == 0) {
+ if ((mMode & MODE_MASK_PICKER) != 0) {
+ ContactsSearchManager.startSearchForResult(this, initialQuery,
+ SUBACTIVITY_FILTER);
+ } else {
+ ContactsSearchManager.startSearch(this, initialQuery);
+ }
}
- parent = parent.getParent();
}
- return null;
}
/**
* search text edit.
*/
protected void onSearchTextChanged() {
+ // Set the proper empty string
+ setEmptyText();
+
Filter filter = mAdapter.getFilter();
filter.filter(getTextFilter());
}
/**
- * Closes search UI if shown, otherwise follows the default "back" behavior.
- */
- @Override
- public void onBackPressed() {
- if (mSearchMode) {
- stopSearchMode();
- } else {
- super.onBackPressed();
- }
- }
-
- /**
* Starts a new activity that will run a search query and display search results.
*/
private void doSearch() {
return;
}
- Intent intent = new Intent(this, getClass());
+ Intent intent = new Intent(this, ContactsListActivity.class);
intent.putExtra(SearchManager.QUERY, query);
- if (isPickerMode()) {
- intent.setAction(ACTION_INTERNAL_SEARCH);
+ if ((mMode & MODE_MASK_PICKER) != 0) {
+ intent.setAction(ACTION_SEARCH_FOR_SHORTCUT);
+ intent.putExtra(SHORTCUT_ACTION_KEY, mShortcutAction);
+ if (mShortcutAction != null) {
+ if (Intent.ACTION_CALL.equals(mShortcutAction)
+ || Intent.ACTION_SENDTO.equals(mShortcutAction)) {
+ intent.putExtra(Insert.PHONE, query);
+ }
+ } else {
+ switch (mQueryMode) {
+ case QUERY_MODE_MAILTO:
+ intent.putExtra(Insert.EMAIL, query);
+ break;
+ case QUERY_MODE_TEL:
+ intent.putExtra(Insert.PHONE, query);
+ break;
+ }
+ }
startActivityForResult(intent, SUBACTIVITY_SEARCH);
} else {
intent.setAction(Intent.ACTION_SEARCH);
mJustCreated = true;
break;
+ case SUBACTIVITY_FILTER:
case SUBACTIVITY_SEARCH:
+ // Pass through results of filter or search UI
if (resultCode == RESULT_OK) {
- returnPickerResult(null, data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME),
- data.getData());
+ setResult(RESULT_OK, data);
+ finish();
}
break;
}
if (!mSearchMode && (mMode & MODE_MASK_NO_FILTER) == 0) {
int unicodeChar = event.getUnicodeChar();
if (unicodeChar != 0) {
- setTextFilter(new String(new int[]{unicodeChar}, 0, 1));
- startSearchMode(false);
+ startSearch(new String(new int[]{unicodeChar}, 0, 1), false, null, false);
return true;
}
}
* Event handler for search UI.
*/
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- if (actionId == EditorInfo.IME_ACTION_GO) {
- doSearch();
+ if (actionId == EditorInfo.IME_ACTION_DONE) {
+ hideSoftKeyboard();
+ if (TextUtils.isEmpty(getTextFilter())) {
+ finish();
+ }
return true;
}
return false;
}
break;
}
-
- case KeyEvent.KEYCODE_SEARCH: {
- if ((mMode & MODE_MASK_NO_FILTER) == 0) {
- if (mSearchMode) {
- stopSearchMode();
- } else {
- startSearchMode(true);
- }
- return true;
- } else {
- return false;
- }
- }
}
return super.onKeyDown(keyCode, event);
}
}
+ /**
+ * Dismisses the soft keyboard when the list takes focus.
+ */
+ public void onFocusChange(View view, boolean hasFocus) {
+ if (view == getListView() && hasFocus) {
+ hideSoftKeyboard();
+ }
+ }
+
+ /**
+ * Dismisses the soft keyboard when the list takes focus.
+ */
+ public boolean onTouch(View view, MotionEvent event) {
+ if (view == getListView()) {
+ hideSoftKeyboard();
+ }
+ return false;
+ }
+
+ /**
+ * Dismisses the search UI along with the keyboard if the filter text is empty.
+ */
+ public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+ if (mSearchMode && keyCode == KeyEvent.KEYCODE_BACK && TextUtils.isEmpty(getTextFilter())) {
+ hideSoftKeyboard();
+ onBackPressed();
+ return true;
+ }
+ return false;
+ }
+
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
- // Hide soft keyboard, if visible
- InputMethodManager inputMethodManager = (InputMethodManager)
- getSystemService(Context.INPUT_METHOD_SERVICE);
- inputMethodManager.hideSoftInputFromWindow(mList.getWindowToken(), 0);
+ hideSoftKeyboard();
if (mMode == MODE_INSERT_OR_EDIT_CONTACT) {
Intent intent;
} else if (mMode == MODE_JOIN_CONTACT && id == JOIN_MODE_SHOW_ALL_CONTACTS_ID) {
mJoinModeShowAllContacts = false;
startQuery();
+ } else if (mSearchMode && mAdapter.isSearchAllContactsItemPosition(position)) {
+ doSearch();
} else if (id > 0) {
final Uri uri = getSelectedUri(position);
if ((mMode & MODE_MASK_PICKER) == 0) {
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
finish();
- } else if (isPickerMode()) {
- Cursor c = (Cursor) mAdapter.getItem(position);
- returnPickerResult(c, c.getString(getSummaryDisplayNameColumnIndex()), uri);
- } else if (mMode == MODE_PICK_PHONE) {
+ } else if (mMode == MODE_PICK_PHONE || mMode == MODE_QUERY_PICK_PHONE) {
Cursor c = (Cursor) mAdapter.getItem(position);
long contactId = c.getLong(PHONE_CONTACT_ID_COLUMN_INDEX);
returnPickerResult(c, c.getString(PHONE_DISPLAY_NAME_COLUMN_INDEX),
ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId));
+ } else if ((mMode & MODE_MASK_PICKER) != 0) {
+ Cursor c = (Cursor) mAdapter.getItem(position);
+ returnPickerResult(c, c.getString(getSummaryDisplayNameColumnIndex()), uri);
} else if (mMode == MODE_PICK_POSTAL
|| mMode == MODE_LEGACY_PICK_POSTAL
|| mMode == MODE_LEGACY_PICK_PHONE) {
}
}
+ private void hideSoftKeyboard() {
+ // Hide soft keyboard, if visible
+ InputMethodManager inputMethodManager = (InputMethodManager)
+ getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputMethodManager.hideSoftInputFromWindow(mList.getWindowToken(), 0);
+ }
+
/**
* @param contactUri In most cases, this should be a lookup {@link Uri}, possibly
* generated through {@link Contacts#getLookupUri(long, String)}.
}
case MODE_QUERY_PICK_TO_VIEW: {
if (mQueryMode == QUERY_MODE_MAILTO) {
- return Uri.withAppendedPath(Email.CONTENT_FILTER_URI, Uri.encode(mQueryData));
+ return Uri.withAppendedPath(Email.CONTENT_FILTER_URI,
+ Uri.encode(mInitialFilter));
} else if (mQueryMode == QUERY_MODE_TEL) {
- return Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(mQueryData));
+ return Uri.withAppendedPath(Phone.CONTENT_FILTER_URI,
+ Uri.encode(mInitialFilter));
}
return CONTACTS_CONTENT_URI_WITH_LETTER_COUNTS;
}
case MODE_QUERY:
case MODE_QUERY_PICK: {
- return getContactFilterUri(mQueryData);
+ return getContactFilterUri(mInitialFilter);
+ }
+ case MODE_QUERY_PICK_PHONE: {
+ return Uri.withAppendedPath(Phone.CONTENT_FILTER_URI,
+ Uri.encode(mInitialFilter));
}
case MODE_GROUP: {
return mGroupUri;
case MODE_LEGACY_PICK_OR_CREATE_PERSON: {
return ContentUris.withAppendedId(People.CONTENT_URI, id);
}
- case MODE_PICK_PHONE: {
+ case MODE_PICK_PHONE:
+ case MODE_QUERY_PICK_PHONE: {
return ContentUris.withAppendedId(Data.CONTENT_URI, id);
}
case MODE_LEGACY_PICK_PHONE: {
case MODE_LEGACY_PICK_OR_CREATE_PERSON: {
return LEGACY_PEOPLE_PROJECTION ;
}
+ case MODE_QUERY_PICK_PHONE:
case MODE_PICK_PHONE: {
return PHONES_PROJECTION;
}
}
void startQuery() {
+ // Set the proper empty string
+ setEmptyText();
+
mAdapter.setLoading(true);
// Cancel any pending queries
}
String[] projection = getProjectionForQuery();
+ if (mSearchMode && TextUtils.isEmpty(getTextFilter())) {
+ mAdapter.changeCursor(new MatrixCursor(projection));
+ return;
+ }
+
String callingPackage = getCallingPackage();
Uri uri = getUriToQuery();
if (!TextUtils.isEmpty(callingPackage)) {
// Kick off the new query
switch (mMode) {
case MODE_GROUP:
- mQueryHandler.startQuery(QUERY_TOKEN, null,
- uri, projection, getContactSelection(), null,
- getSortOrder(projection));
- break;
-
case MODE_DEFAULT:
case MODE_PICK_CONTACT:
case MODE_PICK_OR_CREATE_CONTACT:
case MODE_INSERT_OR_EDIT_CONTACT:
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
- projection, getContactSelection(), null,
- getSortOrder(projection));
+ mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection, getContactSelection(),
+ null, getSortOrder(projection));
break;
case MODE_LEGACY_PICK_PERSON:
case MODE_LEGACY_PICK_OR_CREATE_PERSON:
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
- projection, null, null,
- getSortOrder(projection));
- break;
-
+ case MODE_PICK_POSTAL:
case MODE_QUERY:
- case MODE_QUERY_PICK: {
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
- projection, null, null,
- getSortOrder(projection));
- break;
- }
-
+ case MODE_QUERY_PICK:
+ case MODE_QUERY_PICK_PHONE:
case MODE_QUERY_PICK_TO_VIEW: {
mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection, null, null,
getSortOrder(projection));
case MODE_PICK_PHONE:
case MODE_LEGACY_PICK_PHONE:
mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
- projection, null, null, getSortOrder(projection));
- break;
-
- case MODE_PICK_POSTAL:
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
- projection, null, null, getSortOrder(projection));
+ projection, CLAUSE_ONLY_VISIBLE, null, getSortOrder(projection));
break;
case MODE_LEGACY_PICK_POSTAL:
* @return a cursor with the results of the filter
*/
Cursor doFilter(String filter) {
- final ContentResolver resolver = getContentResolver();
-
String[] projection = getProjectionForQuery();
+ if (mSearchMode && TextUtils.isEmpty(getTextFilter())) {
+ return new MatrixCursor(projection);
+ }
+ final ContentResolver resolver = getContentResolver();
switch (mMode) {
case MODE_DEFAULT:
case MODE_PICK_CONTACT:
if (!TextUtils.isEmpty(filter)) {
uri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(filter));
}
- return resolver.query(uri, projection, null, null,
+ return resolver.query(uri, projection, CLAUSE_ONLY_VISIBLE, null,
getSortOrder(projection));
}
if (activity.mAdapter.mSuggestionsCursorCount == 0
|| !activity.mJoinModeShowAllContacts) {
startQuery(QUERY_TOKEN, null, activity.getContactFilterUri(
- activity.mQueryData),
+ activity.getTextFilter()),
CONTACTS_SUMMARY_PROJECTION,
Contacts._ID + " != " + activity.mQueryAggregateId
+ " AND " + CLAUSE_ONLY_VISIBLE, null,
public TextWithHighlighting textWithHighlighting;
}
- final static class PhotoInfo {
- public int position;
- public long photoId;
-
- public PhotoInfo(int position, long photoId) {
- this.position = position;
- this.photoId = photoId;
- }
- public QuickContactBadge photoView;
- }
-
final static class PinnedHeaderCache {
public TextView titleView;
public ColorStateList textColor;
switch (mMode) {
case MODE_LEGACY_PICK_POSTAL:
case MODE_PICK_POSTAL:
- mDisplaySectionHeaders = false;
- break;
case MODE_LEGACY_PICK_PHONE:
case MODE_PICK_PHONE:
+ case MODE_STREQUENT:
+ case MODE_FREQUENT:
mDisplaySectionHeaders = false;
break;
- default:
- break;
+ }
+
+ if (mSearchMode) {
+ mDisplaySectionHeaders = false;
}
// Do not display the second line of text if in a specific SEARCH query mode, usually for
// matching a specific E-mail or phone number. Any contact details
// shown would be identical, and columns might not even be present
// in the returned cursor.
- if (mQueryMode != QUERY_MODE_NONE) {
+ if (mMode != MODE_QUERY_PICK_PHONE && mQueryMode != QUERY_MODE_NONE) {
mDisplayAdditionalData = false;
}
mDisplayPhotos = true;
setViewResource(R.layout.contacts_list_item_photo);
}
-
- if (mMode == MODE_STREQUENT || mMode == MODE_FREQUENT) {
- mDisplaySectionHeaders = false;
- }
}
public boolean getDisplaySectionHeadersEnabled() {
// This mode mask adds a header and we always want it to show up, even
// if the list is empty, so always claim the list is not empty.
return false;
+ } else if (mSearchMode) {
+ return TextUtils.isEmpty(getTextFilter());
} else {
if (mCursor == null || mLoading) {
// We don't want the empty state to show when loading.
@Override
public int getItemViewType(int position) {
- if (position == 0
- && ((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0
- || (mMode & MODE_MASK_CREATE_NEW) != 0)) {
+ if (position == 0 && (mShowNumberOfContacts || (mMode & MODE_MASK_CREATE_NEW) != 0)) {
return IGNORE_ITEM_VIEW_TYPE;
}
+
if (isShowAllContactsItemPosition(position)) {
return IGNORE_ITEM_VIEW_TYPE;
}
+
+ if (isSearchAllContactsItemPosition(position)) {
+ return IGNORE_ITEM_VIEW_TYPE;
+ }
+
if (getSeparatorId(position) != 0) {
// We don't want the separator view to be recycled.
return IGNORE_ITEM_VIEW_TYPE;
}
+
return super.getItemViewType(position);
}
}
// handle the total contacts item
- if (position == 0 && (mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0) {
+ if (position == 0 && mShowNumberOfContacts) {
return getTotalContactCountView(parent);
}
inflate(R.layout.contacts_list_show_all_item, parent, false);
}
+ if (isSearchAllContactsItemPosition(position)) {
+ return getLayoutInflater().
+ inflate(R.layout.contacts_list_search_all_item, parent, false);
+ }
+
// Handle the separator specially
int separatorId = getSeparatorId(position);
if (separatorId != 0) {
View view = inflater.inflate(R.layout.total_contacts, parent, false);
TextView totalContacts = (TextView) view.findViewById(R.id.totalContactsText);
- TextView searchForMore = (TextView) view.findViewById(R.id.searchForMoreText);
String text;
int count = getRealCount();
- if (mMode == MODE_QUERY || mMode == MODE_QUERY_PICK) {
+ if (mMode == MODE_QUERY || mMode == MODE_QUERY_PICK || mMode == MODE_QUERY_PICK_PHONE) {
text = getQuantityText(count, R.string.listFoundAllContactsZero,
R.plurals.listFoundAllContacts);
- searchForMore.setVisibility(View.GONE);
} else if (mSearchMode && !TextUtils.isEmpty(getTextFilter())) {
text = getQuantityText(count, R.string.listFoundAllContactsZero,
R.plurals.searchFoundContacts);
- searchForMore.setVisibility(View.VISIBLE);
} else {
if (mDisplayOnlyPhones) {
text = getQuantityText(count, R.string.listTotalPhoneContactsZero,
text = getQuantityText(count, R.string.listTotalAllContactsZero,
R.plurals.listTotalAllContacts);
}
- searchForMore.setVisibility(View.GONE);
}
totalContacts.setText(text);
return view;
&& mSuggestionsCursorCount != 0 && position == mSuggestionsCursorCount + 2;
}
+ private boolean isSearchAllContactsItemPosition(int position) {
+ return mSearchMode && position == getCount() - 1;
+ }
+
private int getSeparatorId(int position) {
int separatorId = 0;
if (position == mFrequentSeparatorPos) {
boolean highlightingEnabled = false;
switch(mMode) {
case MODE_PICK_PHONE:
- case MODE_LEGACY_PICK_PHONE: {
+ case MODE_LEGACY_PICK_PHONE:
+ case MODE_QUERY_PICK_PHONE: {
nameColumnIndex = PHONE_DISPLAY_NAME_COLUMN_INDEX;
dataColumnIndex = PHONE_NUMBER_COLUMN_INDEX;
typeColumnIndex = PHONE_TYPE_COLUMN_INDEX;
@Override
public boolean areAllItemsEnabled() {
return mMode != MODE_STARRED
- && (mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) == 0
+ && !mShowNumberOfContacts
&& mSuggestionsCursorCount == 0;
}
@Override
public boolean isEnabled(int position) {
- if ((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0) {
+ if (mShowNumberOfContacts) {
if (position == 0) {
return false;
}
return 0;
}
int superCount = super.getCount();
- if ((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0 && superCount > 0) {
+ if (mShowNumberOfContacts && (mSearchMode || superCount > 0)) {
// We don't want to count this header if it's the only thing visible, so that
// the empty text will display.
superCount++;
}
+
+ if (mSearchMode) {
+ // Last element in the list is the "Find
+ superCount++;
+ }
+
if (mSuggestionsCursorCount != 0) {
// When showing suggestions, we have 2 additional list items: the "Suggestions"
// and "All contacts" headers.
}
private int getRealPosition(int pos) {
- if ((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0) {
+ if (mShowNumberOfContacts) {
pos--;
}
}
}
- mScrollState = scrollState;
if (scrollState == OnScrollListener.SCROLL_STATE_FLING) {
mPhotoLoader.pause();
} else if (mDisplayPhotos) {