SearchView on Join
We can have the small feature with some tweaks.
Right now ContctEntryListFragment is assuming that the first
directory in the fragment is the default directory, while on Join
screen the first one is "suggested contacts" directory. This change
modifies the implementation so that the fragment remove all directories
after the "default" directory intsead.
Also remove the code using MatrixCursor since we don't rely on it
anymore. This improves the performance of Join screen.
Change-Id: Ic232e09fb65bf72d19b069ce047e5a32dcb9f4ad
diff --git a/res/layout-sw580dp/join_contact_picker.xml b/res/layout-sw580dp/join_contact_picker.xml
new file mode 100644
index 0000000..3d9127b
--- /dev/null
+++ b/res/layout-sw580dp/join_contact_picker.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+ class="com.android.contacts.widget.FullHeightLinearLayout"
+ style="@style/ContactPickerLayout"
+ android:orientation="vertical">
+ <!-- See also comments in contact_picker.xml -->
+ <view
+ class="android.widget.SearchView"
+ android:id="@+id/search_view"
+ android:layout_width="match_parent"
+ android:maxWidth="@dimen/contact_picker_search_view_max_width"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="0dip"
+ android:layout_marginRight="@dimen/list_visible_scrollbar_padding"
+ android:paddingRight="0dip"
+ android:iconifiedByDefault="false" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:id="@+id/list_container" />
+</view>
diff --git a/res/layout/join_contact_picker.xml b/res/layout/join_contact_picker.xml
index ea0deaf..ee30525 100644
--- a/res/layout/join_contact_picker.xml
+++ b/res/layout/join_contact_picker.xml
@@ -14,23 +14,13 @@
limitations under the License.
-->
-<view
- xmlns:android="http://schemas.android.com/apk/res/android"
+<view xmlns:android="http://schemas.android.com/apk/res/android"
class="com.android.contacts.widget.FullHeightLinearLayout"
style="@style/ContactPickerLayout"
android:orientation="vertical">
-
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:id="@+id/list_container" />
-
- <View
- android:id="@+id/divider"
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:layout_marginLeft="?attr/contact_browser_list_padding_left"
- android:layout_marginRight="?attr/contact_browser_list_padding_right"
- android:background="?android:attr/dividerHorizontal" />
</view>
diff --git a/res/values-sw580dp/styles.xml b/res/values-sw580dp/styles.xml
index bf5b137..70e2a3b 100644
--- a/res/values-sw580dp/styles.xml
+++ b/res/values-sw580dp/styles.xml
@@ -95,6 +95,8 @@
<style name="JoinContactActivityTheme" parent="ContactPickerTheme" >
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
+ <!-- In the contact picker screen we're using adjustResize but we don't want it here. -->
+ <item name="android:windowSoftInputMode">adjustUnspecified</item>
</style>
<style name="ContactListFilterTheme" parent="@android:Theme.Holo.Light.Dialog">
diff --git a/src/com/android/contacts/activities/ContactSelectionActivity.java b/src/com/android/contacts/activities/ContactSelectionActivity.java
index b5b6c1d..4b59d34 100644
--- a/src/com/android/contacts/activities/ContactSelectionActivity.java
+++ b/src/com/android/contacts/activities/ContactSelectionActivity.java
@@ -107,7 +107,7 @@
if (savedState != null) {
mActionCode = savedState.getInt(KEY_ACTION_CODE);
}
-
+
// Extract relevant information from the intent
mRequest = mIntentResolver.resolveIntent(getIntent());
if (!mRequest.isValid()) {
diff --git a/src/com/android/contacts/activities/JoinContactActivity.java b/src/com/android/contacts/activities/JoinContactActivity.java
index 75a13d0..4a277cb 100644
--- a/src/com/android/contacts/activities/JoinContactActivity.java
+++ b/src/com/android/contacts/activities/JoinContactActivity.java
@@ -24,18 +24,29 @@
import com.android.contacts.list.OnContactPickerActionListener;
import android.app.ActionBar;
+import android.app.ActionBar.LayoutParams;
import android.app.Fragment;
+import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
+import android.text.TextUtils;
import android.util.Log;
+import android.view.LayoutInflater;
import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.SearchView;
+import android.widget.SearchView.OnCloseListener;
+import android.widget.SearchView.OnQueryTextListener;
/**
* An activity that shows a list of contacts that can be joined with the target contact.
*/
-public class JoinContactActivity extends ContactsActivity {
+public class JoinContactActivity extends ContactsActivity
+ implements OnQueryTextListener, OnCloseListener, OnFocusChangeListener {
private static final String TAG = "JoinContactActivity";
@@ -59,6 +70,7 @@
private long mTargetContactId;
private JoinContactListFragment mListFragment;
+ private SearchView mSearchView;
@Override
public void onAttachFragment(Fragment fragment) {
@@ -93,12 +105,7 @@
.commitAllowingStateLoss();
}
- final ActionBar actionBar = getActionBar();
- if (actionBar != null) {
- actionBar.setDisplayShowHomeEnabled(true);
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowTitleEnabled(true);
- }
+ prepareSearchViewAndActionBar();
}
private void setupActionListener() {
@@ -125,6 +132,74 @@
});
}
+ private void prepareSearchViewAndActionBar() {
+ final ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ final View searchViewOnLayout = findViewById(R.id.search_view);
+ if (searchViewOnLayout != null) {
+ searchViewOnLayout.setVisibility(View.GONE);
+ }
+
+ final View searchViewLayout = LayoutInflater.from(actionBar.getThemedContext())
+ .inflate(R.layout.custom_action_bar, null);
+ mSearchView = (SearchView) searchViewLayout.findViewById(R.id.search_view);
+
+ // In order to make the SearchView look like "shown via search menu", we need to
+ // manually setup its state. See also DialtactsActivity.java and ActionBarAdapter.java.
+ mSearchView.setIconifiedByDefault(true);
+ mSearchView.setQueryHint(getString(R.string.hint_findContacts));
+ mSearchView.setIconified(false);
+
+ mSearchView.setOnQueryTextListener(this);
+ mSearchView.setOnCloseListener(this);
+ mSearchView.setOnQueryTextFocusChangeListener(this);
+
+ actionBar.setCustomView(searchViewLayout,
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ actionBar.setDisplayShowCustomEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ } else {
+ mSearchView = (SearchView) findViewById(R.id.search_view);
+ mSearchView.setQueryHint(getString(R.string.hint_findContacts));
+ mSearchView.setOnQueryTextListener(this);
+ mSearchView.setOnQueryTextFocusChangeListener(this);
+ }
+
+ // Clear focus and suppress keyboard show-up.
+ mSearchView.clearFocus();
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ mListFragment.setQueryString(newText, true);
+ return false;
+ }
+
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ return false;
+ }
+
+ @Override
+ public boolean onClose() {
+ if (!TextUtils.isEmpty(mSearchView.getQuery())) {
+ mSearchView.setQuery(null, true);
+ }
+ return true;
+ }
+
+ @Override
+ public void onFocusChange(View view, boolean hasFocus) {
+ switch (view.getId()) {
+ case R.id.search_view: {
+ if (hasFocus) {
+ showInputMethod(mSearchView.findFocus());
+ }
+ }
+ }
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
@@ -156,4 +231,14 @@
mListFragment.onPickerResult(data);
}
}
+
+ private void showInputMethod(View view) {
+ final InputMethodManager imm = (InputMethodManager)
+ getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm != null) {
+ if (!imm.showSoftInput(view, 0)) {
+ Log.w(TAG, "Failed to show soft input method.");
+ }
+ }
+ }
}
diff --git a/src/com/android/contacts/list/ContactEntryListAdapter.java b/src/com/android/contacts/list/ContactEntryListAdapter.java
index cf86dc9..d95a1bc 100644
--- a/src/com/android/contacts/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/list/ContactEntryListAdapter.java
@@ -131,6 +131,28 @@
return partition;
}
+ /**
+ * Remove all directories after the default directory. This is typically used when contacts
+ * list screens are asked to exit the search mode and thus need to remove all remote directory
+ * results for the search.
+ *
+ * This code assumes that the default directory and directories before that should not be
+ * deleted (e.g. Join screen has "suggested contacts" directory before the default director,
+ * and we should not remove the directory).
+ */
+ /* package */ void removeDirectoriesAfterDefault() {
+ final int partitionCount = getPartitionCount();
+ for (int i = partitionCount - 1; i >= 0; i--) {
+ final Partition partition = getPartition(i);
+ if ((partition instanceof DirectoryPartition)
+ && ((DirectoryPartition) partition).getDirectoryId() == Directory.DEFAULT) {
+ break;
+ } else {
+ removePartition(i);
+ }
+ }
+ }
+
private int getPartitionByDirectoryId(long id) {
int count = getPartitionCount();
for (int i = 0; i < count; i++) {
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index 8db477e..5ee25a2 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -613,12 +613,10 @@
mAdapter.clearPartitions();
if (!flag) {
- // If we are switching from search to regular display,
- // remove all directory partitions (except the default one).
- int count = mAdapter.getPartitionCount();
- for (int i = count; --i >= 1;) {
- mAdapter.removePartition(i);
- }
+ // If we are switching from search to regular display, remove all directory
+ // partitions after default one, assuming they are remote directories which
+ // should be cleaned up on exiting the search mode.
+ mAdapter.removeDirectoriesAfterDefault();
}
mAdapter.configureDefaultPartition(false, flag);
}
diff --git a/src/com/android/contacts/list/JoinContactListAdapter.java b/src/com/android/contacts/list/JoinContactListAdapter.java
index bfe8c53..80ddc83 100644
--- a/src/com/android/contacts/list/JoinContactListAdapter.java
+++ b/src/com/android/contacts/list/JoinContactListAdapter.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.content.CursorLoader;
import android.database.Cursor;
-import android.database.MatrixCursor;
import android.net.Uri;
import android.net.Uri.Builder;
import android.provider.ContactsContract;
@@ -53,7 +52,6 @@
@Override
protected void addPartitions() {
-
// Partition 0: suggestions
addPartition(false, true);
@@ -69,11 +67,11 @@
public void configureLoader(CursorLoader cursorLoader, long directoryId) {
JoinContactLoader loader = (JoinContactLoader) cursorLoader;
- Builder builder = Contacts.CONTENT_URI.buildUpon();
+ final Builder builder = Contacts.CONTENT_URI.buildUpon();
builder.appendEncodedPath(String.valueOf(mTargetContactId));
builder.appendEncodedPath(AggregationSuggestions.CONTENT_DIRECTORY);
- String filter = getQueryString();
+ final String filter = getQueryString();
if (!TextUtils.isEmpty(filter)) {
builder.appendEncodedPath(Uri.encode(filter));
}
@@ -84,13 +82,22 @@
// TODO simplify projection
loader.setProjection(getProjection(false));
- Uri allContactsUri = buildSectionIndexerUri(Contacts.CONTENT_URI).buildUpon()
+ final Uri allContactsUri;
+ if (!TextUtils.isEmpty(filter)) {
+ allContactsUri = buildSectionIndexerUri(Contacts.CONTENT_FILTER_URI).buildUpon()
+ .appendEncodedPath(Uri.encode(filter))
.appendQueryParameter(
ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
.build();
+ } else {
+ allContactsUri = buildSectionIndexerUri(Contacts.CONTENT_URI).buildUpon()
+ .appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
+ .build();
+ }
loader.setUri(allContactsUri);
loader.setSelection(Contacts._ID + "!=?");
- loader.setSelectionArgs(new String[]{String.valueOf(mTargetContactId)});
+ loader.setSelectionArgs(new String[]{ String.valueOf(mTargetContactId) });
if (getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY) {
loader.setSortOrder(Contacts.SORT_KEY_PRIMARY);
} else {
@@ -120,7 +127,7 @@
@Override
public int getViewTypeCount() {
- return super.getViewTypeCount() + 1;
+ return super.getViewTypeCount();
}
@Override
@@ -173,14 +180,14 @@
protected void bindView(View itemView, int partition, Cursor cursor, int position) {
switch (partition) {
case PARTITION_SUGGESTIONS: {
- final ContactListItemView view = (ContactListItemView)itemView;
+ final ContactListItemView view = (ContactListItemView) itemView;
view.setSectionHeader(null);
bindPhoto(view, partition, cursor);
bindName(view, cursor);
break;
}
case PARTITION_ALL_CONTACTS: {
- final ContactListItemView view = (ContactListItemView)itemView;
+ final ContactListItemView view = (ContactListItemView) itemView;
bindSectionHeaderAndDivider(view, position, cursor);
bindPhoto(view, partition, cursor);
bindName(view, cursor);
diff --git a/src/com/android/contacts/list/JoinContactListFragment.java b/src/com/android/contacts/list/JoinContactListFragment.java
index 5b27bdf..7c6767b 100644
--- a/src/com/android/contacts/list/JoinContactListFragment.java
+++ b/src/com/android/contacts/list/JoinContactListFragment.java
@@ -26,6 +26,7 @@
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract.Contacts;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -103,7 +104,10 @@
configureAdapter();
getLoaderManager().initLoader(DISPLAY_NAME_LOADER, null, mLoaderCallbacks);
- getLoaderManager().initLoader(JoinContactListAdapter.PARTITION_ALL_CONTACTS,
+
+ // When this method is called, Uri to be used may be changed. We should use restartLoader()
+ // to load the parameter again.
+ getLoaderManager().restartLoader(JoinContactListAdapter.PARTITION_ALL_CONTACTS,
null, mLoaderCallbacks);
}
@@ -167,4 +171,11 @@
mTargetContactId = savedState.getLong(KEY_TARGET_CONTACT_ID);
}
}
+
+ @Override
+ public void setQueryString(String queryString, boolean delaySelection) {
+ super.setQueryString(queryString, delaySelection);
+
+ setSearchMode(!TextUtils.isEmpty(queryString));
+ }
}
diff --git a/src/com/android/contacts/list/JoinContactLoader.java b/src/com/android/contacts/list/JoinContactLoader.java
index 2f1f9b0..c43560e 100644
--- a/src/com/android/contacts/list/JoinContactLoader.java
+++ b/src/com/android/contacts/list/JoinContactLoader.java
@@ -20,6 +20,7 @@
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
+import android.util.Log;
/**
* A specialized loader for the Join Contacts UI. It executes two queries:
@@ -29,7 +30,7 @@
private String[] mProjection;
private Uri mSuggestionUri;
- private MatrixCursor mSuggestionsCursor;
+ private Cursor mSuggestionsCursor;
public JoinContactLoader(Context context) {
super(context, null, null, null, null, null);
@@ -53,28 +54,8 @@
public Cursor loadInBackground() {
// First execute the suggestions query, then call super.loadInBackground
// to load the entire list
- mSuggestionsCursor = loadSuggestions();
+ mSuggestionsCursor = getContext().getContentResolver()
+ .query(mSuggestionUri, mProjection, null, null, null);
return super.loadInBackground();
}
-
- /**
- * Loads join suggestions into a MatrixCursor.
- */
- private MatrixCursor loadSuggestions() {
- Cursor cursor = getContext().getContentResolver().query(mSuggestionUri, mProjection,
- null, null, null);
- try {
- MatrixCursor matrix = new MatrixCursor(mProjection);
- Object[] row = new Object[mProjection.length];
- while (cursor.moveToNext()) {
- for (int i = 0; i < row.length; i++) {
- row[i] = cursor.getString(i);
- }
- matrix.addRow(row);
- }
- return matrix;
- } finally {
- cursor.close();
- }
- }
}
\ No newline at end of file