1331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner/** 2331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * Copyright (c) 2009, Google Inc. 3331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * 4331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * Licensed under the Apache License, Version 2.0 (the "License"); 5331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * you may not use this file except in compliance with the License. 6331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * You may obtain a copy of the License at 7331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * 8331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * http://www.apache.org/licenses/LICENSE-2.0 9331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * 10331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * Unless required by applicable law or agreed to in writing, software 11331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * distributed under the License is distributed on an "AS IS" BASIS, 12331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * See the License for the specific language governing permissions and 14331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * limitations under the License. 15331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner */ 16331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 17331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerpackage com.android.mms.ui; 18331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 19ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagnerimport java.util.HashMap; 20f114492537753bc68640d4a0d403861387296bcbMark Wagnerimport java.util.regex.Matcher; 21f114492537753bc68640d4a0d403861387296bcbMark Wagnerimport java.util.regex.Pattern; 22f114492537753bc68640d4a0d403861387296bcbMark Wagner 238447aa1d84a469e8938b53a60b0763633b08ae98Tom Taylorimport com.android.mms.MmsApp; 24331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport com.android.mms.R; 25331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.app.ListActivity; 26331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.app.SearchManager; 27331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.content.AsyncQueryHandler; 28331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.content.ContentResolver; 29331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.content.Context; 30331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.content.Intent; 31331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.database.Cursor; 32331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.graphics.Color; 33331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.graphics.Typeface; 34331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.net.Uri; 35331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.os.Bundle; 36f114492537753bc68640d4a0d403861387296bcbMark Wagnerimport android.provider.SearchRecentSuggestions; 37f114492537753bc68640d4a0d403861387296bcbMark Wagner 38f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport android.provider.Telephony; 39331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.text.SpannableString; 40331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.text.TextPaint; 41331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.text.style.ForegroundColorSpan; 42331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.text.style.StyleSpan; 43331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.util.AttributeSet; 44331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.view.LayoutInflater; 45331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.view.View; 46331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.view.ViewGroup; 47331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.widget.CursorAdapter; 48bc878528ed57d19bec31553cd75efaac1e0d3a5bMark Wagnerimport android.widget.ListView; 49331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport android.widget.TextView; 50331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 51331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport com.android.mms.data.Contact; 52ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagnerimport com.android.mms.data.Contact.UpdateListener; 53331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerimport com.android.mms.ui.ComposeMessageActivity; 54331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 55331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner/*** 56331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * Presents a List of search results. Each item in the list represents a thread which 57331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * matches. The item contains the contact (or phone number) as the "title" and a 58331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * snippet of what matches, below. The snippet is taken from the most recent part of 59331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * the conversation that has a match. Each match within the visible portion of the 60331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * snippet is highlighted. 61331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner */ 62331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 63331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagnerpublic class SearchActivity extends ListActivity 64331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner{ 65ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner private AsyncQueryHandler mQueryHandler; 66ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner 67ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner // Track which TextView's show which Contact objects so that we can update 68ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner // appropriately when the Contact gets fully loaded. 69ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner private HashMap<Contact, TextView> mContactMap = new HashMap<Contact, TextView>(); 70ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner 71331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 72331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner /* 73331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * Subclass of TextView which displays a snippet of text which matches the full text and 74331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * highlights the matches within the snippet. 75331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner */ 76331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner public static class TextViewSnippet extends TextView { 77331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner private static String sEllipsis = "\u2026"; 78bc878528ed57d19bec31553cd75efaac1e0d3a5bMark Wagner 79331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner private static int sTypefaceHighlight = Typeface.BOLD; 80331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 81331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner private String mFullText; 82331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner private String mTargetString; 83f114492537753bc68640d4a0d403861387296bcbMark Wagner private Pattern mPattern; 84331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 85331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner public TextViewSnippet(Context context, AttributeSet attrs) { 86331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner super(context, attrs); 87331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } 88331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 89331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner public TextViewSnippet(Context context) { 90331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner super(context); 91331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } 92331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 93331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner public TextViewSnippet(Context context, AttributeSet attrs, int defStyle) { 94331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner super(context, attrs, defStyle); 95331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } 96331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 97331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner /** 98331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * We have to know our width before we can compute the snippet string. Do that 99331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * here and then defer to super for whatever work is normally done. 100331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner */ 101331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner @Override 102331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 103331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner String fullTextLower = mFullText.toLowerCase(); 104331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner String targetStringLower = mTargetString.toLowerCase(); 105b2de23b71c774412d228d20ff4b941f082df3725Mark Wagner 106f114492537753bc68640d4a0d403861387296bcbMark Wagner int startPos = 0; 107331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner int searchStringLength = targetStringLower.length(); 108331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner int bodyLength = fullTextLower.length(); 109331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 110f114492537753bc68640d4a0d403861387296bcbMark Wagner Matcher m = mPattern.matcher(mFullText); 111f114492537753bc68640d4a0d403861387296bcbMark Wagner if (m.find(0)) { 112f114492537753bc68640d4a0d403861387296bcbMark Wagner startPos = m.start(); 113f114492537753bc68640d4a0d403861387296bcbMark Wagner } 114f114492537753bc68640d4a0d403861387296bcbMark Wagner 115331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner TextPaint tp = getPaint(); 116331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 117331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner float searchStringWidth = tp.measureText(mTargetString); 118331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner float textFieldWidth = getWidth(); 119331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 120331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner String snippetString = null; 121331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner if (searchStringWidth > textFieldWidth) { 122331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner snippetString = mFullText.substring(startPos, startPos + searchStringLength); 123331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } else { 124331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner float ellipsisWidth = tp.measureText(sEllipsis); 125331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner textFieldWidth -= (2F * ellipsisWidth); // assume we'll need one on both ends 126331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 127331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner int offset = -1; 128331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner int start = -1; 129331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner int end = -1; 130331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner /* TODO: this code could be made more efficient by only measuring the additional 131331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * characters as we widen the string rather than measuring the whole new 132331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner * string each time. 133331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner */ 134331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner while (true) { 135331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner offset += 1; 136331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 137331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner int newstart = Math.max(0, startPos - offset); 138331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner int newend = Math.min(bodyLength, startPos + searchStringLength + offset); 139331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 140331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner if (newstart == start && newend == end) { 141331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner // if we couldn't expand out any further then we're done 142331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner break; 143331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } 144331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner start = newstart; 145331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner end = newend; 146331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 147331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner // pull the candidate string out of the full text rather than body 148331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner // because body has been toLower()'ed 149331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner String candidate = mFullText.substring(start, end); 150331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner if (tp.measureText(candidate) > textFieldWidth) { 151331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner // if the newly computed width would exceed our bounds then we're done 152331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner // do not use this "candidate" 153331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner break; 154331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } 155331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 156331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner snippetString = String.format( 157331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner "%s%s%s", 158331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner start == 0 ? "" : sEllipsis, 159331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner candidate, 160331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner end == bodyLength ? "" : sEllipsis); 161331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } 162331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } 163331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 164331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner SpannableString spannable = new SpannableString(snippetString); 165331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner int start = 0; 166f114492537753bc68640d4a0d403861387296bcbMark Wagner 167f114492537753bc68640d4a0d403861387296bcbMark Wagner m = mPattern.matcher(snippetString); 168f114492537753bc68640d4a0d403861387296bcbMark Wagner while (m.find(start)) { 169f114492537753bc68640d4a0d403861387296bcbMark Wagner spannable.setSpan(new StyleSpan(sTypefaceHighlight), m.start(), m.end(), 0); 170f114492537753bc68640d4a0d403861387296bcbMark Wagner start = m.end(); 171331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } 172331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner setText(spannable); 173331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 174331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner // do this after the call to setText() above 175331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner super.onLayout(changed, left, top, right, bottom); 176331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } 177331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 178331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner public void setText(String fullText, String target) { 179f114492537753bc68640d4a0d403861387296bcbMark Wagner // Use a regular expression to locate the target string 180f114492537753bc68640d4a0d403861387296bcbMark Wagner // within the full text. The target string must be 181f114492537753bc68640d4a0d403861387296bcbMark Wagner // found as a word start so we use \b which matches 182f114492537753bc68640d4a0d403861387296bcbMark Wagner // word boundaries. 183459ad1ea05610f7c071f65da6842e00e21bd5eafMark Wagner String patternString = "\\b" + Pattern.quote(target); 184f114492537753bc68640d4a0d403861387296bcbMark Wagner mPattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE); 185f114492537753bc68640d4a0d403861387296bcbMark Wagner 186331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner mFullText = fullText; 187331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner mTargetString = target; 188331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner requestLayout(); 189331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } 190331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } 191331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 192ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner Contact.UpdateListener mContactListener = new Contact.UpdateListener() { 193ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner public void onUpdate(Contact updated) { 194ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner TextView tv = mContactMap.get(updated); 195ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner if (tv != null) { 196ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner tv.setText(updated.getNameAndNumber()); 197ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner } 198ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner } 199ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner }; 200ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner 201ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner @Override 202ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner public void onStop() { 203ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner super.onStop(); 204ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner Contact.removeListener(mContactListener); 205ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner } 206ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner 207ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner @Override 208331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner public void onCreate(Bundle icicle) 209331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner { 210331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner super.onCreate(icicle); 211d808dd7a449839282d13426d353b7007122ddb64Mark Wagner setContentView(R.layout.search_activity); 212331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 213f114492537753bc68640d4a0d403861387296bcbMark Wagner String searchStringParameter = getIntent().getStringExtra(SearchManager.QUERY); 214f114492537753bc68640d4a0d403861387296bcbMark Wagner if (searchStringParameter == null) { 215f114492537753bc68640d4a0d403861387296bcbMark Wagner searchStringParameter = getIntent().getStringExtra("intent_extra_data_key" /*SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA*/); 216f114492537753bc68640d4a0d403861387296bcbMark Wagner } 2178447aa1d84a469e8938b53a60b0763633b08ae98Tom Taylor final String searchString = 2183e48eec660aa5eaf3dfd186f75a648ea188d1f7bMark Wagner searchStringParameter != null ? searchStringParameter.trim() : searchStringParameter; 219331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner ContentResolver cr = getContentResolver(); 220331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 221f114492537753bc68640d4a0d403861387296bcbMark Wagner searchStringParameter = searchStringParameter.trim(); 222bc878528ed57d19bec31553cd75efaac1e0d3a5bMark Wagner final ListView listView = getListView(); 223bc878528ed57d19bec31553cd75efaac1e0d3a5bMark Wagner listView.setItemsCanFocus(true); 224bc878528ed57d19bec31553cd75efaac1e0d3a5bMark Wagner listView.setFocusable(true); 225bc878528ed57d19bec31553cd75efaac1e0d3a5bMark Wagner listView.setClickable(true); 226bc878528ed57d19bec31553cd75efaac1e0d3a5bMark Wagner 227b2de23b71c774412d228d20ff4b941f082df3725Mark Wagner // I considered something like "searching..." but typically it will 228b2de23b71c774412d228d20ff4b941f082df3725Mark Wagner // flash on the screen briefly which I found to be more distracting 229b2de23b71c774412d228d20ff4b941f082df3725Mark Wagner // than beneficial. 230b2de23b71c774412d228d20ff4b941f082df3725Mark Wagner // This gets updated when the query completes. 231b2de23b71c774412d228d20ff4b941f082df3725Mark Wagner setTitle(""); 232b2de23b71c774412d228d20ff4b941f082df3725Mark Wagner 233ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner Contact.addListener(mContactListener); 234ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner 235f06d26b3491ea4708e8e1b8214f2ef1cf9d4575bMark Wagner // When the query completes cons up a new adapter and set our list adapter to that. 236331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner mQueryHandler = new AsyncQueryHandler(cr) { 237331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner protected void onQueryComplete(int token, Object cookie, Cursor c) { 23869f9edb80d24a50a0f98d7fcaccdc3e19cc903beTom Taylor if (c == null) { 23969f9edb80d24a50a0f98d7fcaccdc3e19cc903beTom Taylor return; 24069f9edb80d24a50a0f98d7fcaccdc3e19cc903beTom Taylor } 241331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner final int threadIdPos = c.getColumnIndex("thread_id"); 242331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner final int addressPos = c.getColumnIndex("address"); 243331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner final int bodyPos = c.getColumnIndex("body"); 244331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner final int rowidPos = c.getColumnIndex("_id"); 245331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 246b2de23b71c774412d228d20ff4b941f082df3725Mark Wagner int cursorCount = c.getCount(); 247b2de23b71c774412d228d20ff4b941f082df3725Mark Wagner setTitle(getResources().getQuantityString( 248b2de23b71c774412d228d20ff4b941f082df3725Mark Wagner R.plurals.search_results_title, 249b2de23b71c774412d228d20ff4b941f082df3725Mark Wagner cursorCount, 250b2de23b71c774412d228d20ff4b941f082df3725Mark Wagner cursorCount, 251b2de23b71c774412d228d20ff4b941f082df3725Mark Wagner searchString)); 252b2de23b71c774412d228d20ff4b941f082df3725Mark Wagner 253f1ab045ff991e1ec3e4161d213627b8629971c0eTom Taylor // Note that we're telling the CursorAdapter not to do auto-requeries. If we 254f1ab045ff991e1ec3e4161d213627b8629971c0eTom Taylor // want to dynamically respond to changes in the search results, 255f1ab045ff991e1ec3e4161d213627b8629971c0eTom Taylor // we'll have have to add a setOnDataSetChangedListener(). 256f1ab045ff991e1ec3e4161d213627b8629971c0eTom Taylor setListAdapter(new CursorAdapter(SearchActivity.this, 257f1ab045ff991e1ec3e4161d213627b8629971c0eTom Taylor c, false /* no auto-requery */) { 258331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner @Override 259331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner public void bindView(View view, Context context, Cursor cursor) { 26081c7152ef1638673d215e36a126b4bf8db236abeMark Wagner final TextView title = (TextView)(view.findViewById(R.id.title)); 26181c7152ef1638673d215e36a126b4bf8db236abeMark Wagner final TextViewSnippet snippet = (TextViewSnippet)(view.findViewById(R.id.subtitle)); 262331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 263331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner String address = cursor.getString(addressPos); 2646fb305d028ceaf74e4237687a1337396bb01b056Mark Wagner Contact contact = address != null ? Contact.get(address, false) : null; 26581c7152ef1638673d215e36a126b4bf8db236abeMark Wagner 2666fb305d028ceaf74e4237687a1337396bb01b056Mark Wagner String titleString = contact != null ? contact.getNameAndNumber() : ""; 26781c7152ef1638673d215e36a126b4bf8db236abeMark Wagner title.setText(titleString); 268331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 269331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner snippet.setText(cursor.getString(bodyPos), searchString); 270331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 271331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner // if the user touches the item then launch the compose message 272331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner // activity with some extra parameters to highlight the search 273331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner // results and scroll to the latest part of the conversation 274331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner // that has a match. 275331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner final long threadId = cursor.getLong(threadIdPos); 276331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner final long rowid = cursor.getLong(rowidPos); 277331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 278331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner view.setOnClickListener(new View.OnClickListener() { 279331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner public void onClick(View v) { 280331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner final Intent onClickIntent = new Intent(SearchActivity.this, ComposeMessageActivity.class); 281331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner onClickIntent.putExtra("thread_id", threadId); 282331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner onClickIntent.putExtra("highlight", searchString); 283331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner onClickIntent.putExtra("select_id", rowid); 284331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner startActivity(onClickIntent); 285331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } 286331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner }); 287331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } 288331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 289331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner @Override 290331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner public View newView(Context context, Cursor cursor, ViewGroup parent) { 291331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner LayoutInflater inflater = LayoutInflater.from(context); 292bc878528ed57d19bec31553cd75efaac1e0d3a5bMark Wagner View v = inflater.inflate(R.layout.search_item, parent, false); 293331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner return v; 294331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } 295331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 296331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner }); 297417d3ee9970f4cef1a42650c49d97c6d85c35204Mark Wagner 298417d3ee9970f4cef1a42650c49d97c6d85c35204Mark Wagner // ListView seems to want to reject the setFocusable until such time 299417d3ee9970f4cef1a42650c49d97c6d85c35204Mark Wagner // as the list is not empty. Set it here and request focus. Without 300417d3ee9970f4cef1a42650c49d97c6d85c35204Mark Wagner // this the arrow keys (and trackball) fail to move the selection. 301417d3ee9970f4cef1a42650c49d97c6d85c35204Mark Wagner listView.setFocusable(true); 302417d3ee9970f4cef1a42650c49d97c6d85c35204Mark Wagner listView.setFocusableInTouchMode(true); 303417d3ee9970f4cef1a42650c49d97c6d85c35204Mark Wagner listView.requestFocus(); 3048447aa1d84a469e8938b53a60b0763633b08ae98Tom Taylor 3058447aa1d84a469e8938b53a60b0763633b08ae98Tom Taylor // Remember the query if there are actual results 3068447aa1d84a469e8938b53a60b0763633b08ae98Tom Taylor if (cursorCount > 0) { 307f114492537753bc68640d4a0d403861387296bcbMark Wagner SearchRecentSuggestions recent = ((MmsApp)getApplication()).getRecentSuggestions(); 308f114492537753bc68640d4a0d403861387296bcbMark Wagner if (recent != null) { 309f114492537753bc68640d4a0d403861387296bcbMark Wagner recent.saveRecentQuery( 310f114492537753bc68640d4a0d403861387296bcbMark Wagner searchString, 311f114492537753bc68640d4a0d403861387296bcbMark Wagner getString(R.string.search_history, 312f114492537753bc68640d4a0d403861387296bcbMark Wagner cursorCount, searchString)); 313f114492537753bc68640d4a0d403861387296bcbMark Wagner } 3148447aa1d84a469e8938b53a60b0763633b08ae98Tom Taylor } 315331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } 316331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner }; 317331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 318331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner // don't pass a projection since the search uri ignores it 319f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylor Uri uri = Telephony.MmsSms.SEARCH_URI.buildUpon() 3206be18bedb5b87dbbcdb54f37d5a0945bd0f71377Tom Taylor .appendQueryParameter("pattern", searchString).build(); 321331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 322331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner // kick off a query for the threads which match the search string 323f06d26b3491ea4708e8e1b8214f2ef1cf9d4575bMark Wagner mQueryHandler.startQuery(0, null, uri, null, null, null, null); 324331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner 325331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner } 326331864544ec51ba6807fc5471cc6d537b7fef198Mark Wagner} 327