I need to create a MultiAutoCompleteTextView with the phone numbers of the contacts on a user's device. What I need is similar to gmail; except with gmail email addresses are used. For the contacts, I have the following needs:
each phone number must be an entry. So if John has 3 numbers (home, cell, work), they show as 3 entries
each entry is searchable by phone number or by first/last name of person
To create my adapter, I try to modify the one provided by Google but when I download the sample, it does not compile (kind of a crappy experience when the thing is right out of the box, but I am trying troubleshoot it). Then using the sample at http://developer.android.com/reference/android/widget/MultiAutoCompleteTextView.html I will bound my MultiAutoCompleteTextView to the adapter. At this point, I am not sure how to convert the adapter to match my needs (i.e. search contacts by name or phone and to retrieve the numbers). So my call for help is this: has anyone successfully done this and don't mind sharing their code? Or Does anyone know how I can modify the linked adapter to give me phone numbers, which I can search by name or phone? And third, will the adapter work with MultiAutoCompleteTextView?
Note
In asking this question, I have made certain assumptions on how Google is implementing their MultiAutoCompleteTextView for emails. Does anyone know if that code is open source? Does anyone know if my assumptions are correct? Will my idea for implementing my contact phone MultiAutoCompleteTextView work?
UPDATE
So I have come a long way since asking the question. I am now using the answer at AutoComplete with name and number as in native sms app Android . But I am trying to convert the implementation to MultiAutoCompleteTextView but it's not allowing for multiple entries. Does anyone know how I might finish this?
UPDATE 2
Refer to AutoComplete with name and number as in native sms app Android :
My MultiAutoCompleteTextView is presently kind of working: it's allowing for multiple entries. I simply replaced AutoCompleteTextView with MultiAutoCompleteTextView, and I ignored the other answer's onItemClick suggestion. It's working, kind of. Except, the data I get is not the nice formatted elements that you see in the gmail EditText. So back to the original question: how is Google doing it? I don't want to spend time explaining how the gmail compose editText looks as the relevant reader can readily verify this. In their EditText I can enter four contacts and then with random access click on one to delete it. I want to be able to do that. How?
try this:
final Resources res = getResources();
LinearLayout ll = new LinearLayout(this);
AutoCompleteTextView tv = new AutoCompleteTextView(this);
tv.setThreshold(1);
String[] from = { Phone.DISPLAY_NAME };
int[] to = { android.R.id.text1 };
SimpleCursorAdapter a = new SimpleCursorAdapter(this, android.R.layout.simple_dropdown_item_1line, null, from, to, 0);
a.setStringConversionColumn(2); // Phone.NUMBER
ViewBinder viewBinder = new ViewBinder() {
#Override
public boolean setViewValue(View v, Cursor c, int index) {
TextView tv = (TextView) v;
int typeInt = c.getInt(3); // Phone.TYPE
CharSequence type = Phone.getTypeLabel(res, typeInt, null);
// Phone.DISPLAY_NAME + Phone.NUMBER + type
tv.setSingleLine(false);
tv.setText(c.getString(1) + "\n" + c.getString(2) + " " + type);
return true;
}
};
a.setViewBinder(viewBinder);
FilterQueryProvider provider = new FilterQueryProvider() {
#Override
public Cursor runQuery(CharSequence constraint) {
// run in the background thread
Log.d(TAG, "runQuery constraint: " + constraint);
if (constraint == null) {
return null;
}
ContentResolver cr = getContentResolver();
Uri uri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, constraint.toString());
String[] proj = { BaseColumns._ID, Phone.DISPLAY_NAME, Phone.NUMBER, Phone.TYPE, };
return cr.query(uri, proj, null, null, null);
}
};
a.setFilterQueryProvider(provider);
tv.setAdapter(a);
ll.addView(tv, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
setContentView(ll);
Related
I've pulled out most of my hair now and really needs some help
before I go completely bold
I'm trying to launch an action picker to select a Contact that has a phone number.
When that contact is selected, I want to extract the name and phone number.
But this only happens for some contacts, not all.
The code is rougly as follows:
Select contact:
Intent contactPicker = new Intent(Intent.ACTION_PICK, contactsContract.CommonDataKinds.Phone.CONTENT_URI);
startActivityForResult(contactPicker, REQ_PICK_CONTACT);
Extract id (notice getData().getLastPathSegment():
onContactForImportPicked(intent.getData().getLastPathSegment());
and then I try to fetch this contact:
String[] fields = new String[] {
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY
};
Cursor cursor = content.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
fields,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=?", new String[] { id }, // SEE BELOW
null);
It this point, many contacts are fetched correctly, but a lot is
also non-existing. cursor.getCount() == 0. In the 'SEE BELOW'
section above, I've tried various other fields, linke
Contact._ID, Phone._ID etc etc.
Any idea why some contacts are not fetched with this method?
When I use your code, I don't even get the contact I selected correctly. The intent data contains the URI to the data you want, so you can use that directly.
Try --
Cursor cursor = content.query(data.getData(),
fields,
null,
null,
null);
I m fetching data from a table in SQLite.
This is my code
String[] sColoumns = { "_id", "userloginid", "password", "firstname", "lastname", "email", "address"};
Cursor cursor = db.query("users", sColoumns, null,null,null,null,null);
The query returns all rows of the table in the cursor.
Now i want to pull the rows one by one from the cursor or load them into a data table and then pull them one by one(like in visual studio).
Is there anything available or anything i can do to achieve this?
thanks in advance.
There are two ways to answer your question.
1) You can simply iterate over the cursor and do whatever you want with the row data:
String[] sColoumns = { "_id", "userloginid", "password", "firstname", "lastname", "email", "address"};
Cursor cursor = db.query("users", sColoumns, null,null,null,null,null);
try {
int firstNameColIndex = cursor.getColumnIndex("firstname");
int lastNameColIndex = cursor.getColumnIndex("lastname");
while (cursor.moveToNext()) {
String fullName = cursor.getString(firstNameColIndex) + cursor.getString(lastNameColIndex);
}
} finally {
cursor.close();
}
2) A common use case is to display data in a list view. Since Android 3.0 Honeycomb (and the Android Support/Compatiblity lib for earlier versions) the best practice is to use a Loader for taking care of managing the cursor. For example it will take care of updating the list view if data is updated in a content provider. I recommend reading http://developer.android.com/guide/topics/fundamentals/loaders.html (see links to samples at the bottom) to understand this topic.
I am using Filter with ListView which is populated trough Contact data which contains Names and Number.
Now i got two problems when i type a text into EditText which in turns fires adapter.getFilter().filter(s.toString())
1) When i type 'aa' latter (in my code )
i can see name starting from 'aa' for example aakruti , but at the
same time i am able to view email addresses too , which i don't wanted
to make it visible when a filter is fired.
2) When i type 'aa' latter (in phone's inbuilt contact list)
i can see name starting from 'aa' for example aakruti
but i am missing one name i.e. S A T Y A ( which is shown by contact
search when i type 'aa' latter into it )
here is my filter query , inside runQueryOnBackgroundThread
StringBuilder buffer = null;
String[] args = null;
if (constraint != null) {
buffer = new StringBuilder();
buffer.append("UPPER(");
buffer.append(ContactsContract.Contacts.DISPLAY_NAME);
buffer.append(") GLOB ?");
args = new String[] { constraint.toString().toUpperCase() + "*" };
}
String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
System.out.println(buffer);
return mContent.query(
ContactsContract.Contacts.CONTENT_URI,
projection,
buffer == null ? null : buffer.toString(),
args,sortOrder
);
projection data
public static String[] projection = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME
};
EDIT
So far i tried to access
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME
ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME
ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME
But GIVEN_NAME displays few email address too and even it shows contact name which has email address
for example ,
xyz#gmail.com
Raul Jakson (which only has email address no contact number)
Raul Jakson ( i see this name twice as it has two different email address , but i wanna see it as name )
so can anyone tell me how can i limit the email addresses and show only NAMES which has only contact phone numbers ?
Firstly, the emails show up because that contact has no name. In these cases, Android will use the email as the display name. To avoid showing these, use GIVEN_NAME and FAMILY_NAME. See the api docs for StructuredName.
Secondly, you don't find SATIYA as your query is looking for a DISPLAY_NAME that begins with AA. SATIYA is not a word, it's a sequence of initials, e.g. S A T I Y A. If you want to find these, then you'll have to craft your query to cater for this. You could search for *A*A*, but you'll probably get many others hits too. I suspect Android is doing some variant of an initial+surname search where BO would find Barack Obama.
I have some contacts autocomplete and autosearch algorithm working for my android app.
First some xml to define the text view for the input:
<AutoCompleteTextView
a:id="#+id/recipientBody"
a:layout_width="0dip"
a:layout_height="wrap_content"
a:layout_weight="1.0"
a:nextFocusRight="#+id/smsRecipientButton"
a:hint="#string/sms_to_whom"
a:maxLines="10"
/>
And now I setup the text view
AutoCompleteTextView recip =
(AutoCompleteTextView) findViewById(R.id.recipientBody);
ArrayAdapter<String> adapter =
new ArrayAdapter<String>(this, R.layout.list_item, getAllContacts());
recip.setAdapter(adapter);
And now the actual algorithm that searches for a contact that matches the input:
private List<String> getAllContacts() {
List<String> contacts = new ArrayList<String>();
ContentResolver cr = getContentResolver();
Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
if (Integer.parseInt(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
Cursor pCursor = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[]{contactId}, null);
while (pCursor.moveToNext()) {
String phoneNumber = pCursor.getString(pCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
contacts.add(phoneNumber + " ( " + displayName + " )");
}
pCursor.close();
}
}
}
return contacts;
}
This works fine for both contact number and name input. But there is still a problem. The user can input multiple phone numbers. But when one contact is applied to the text view it cannot search again, because the algorithm takes the whole string.
How can I solve that?
EDIT:
Well, I thought about it for a while and spotted a problem with my solution - there's no place where you could insert the contact from the completion list into the TextView.
The solution seems to be MultiAutoCompleteTextView, this thing is designed for solving your problem.
Sorry for confusion!
For me, it looks like you need a custom adapter.
You may extend ArrayAdapter<String> and implement getFilter() - of course you will also need a custom filter (extending Filter) which instance you will return from that method.
Filter's performFiltering method has one parameter - the string for which the list of suggestion is needed. You need to take the part after the last comma (or whatever character are you using as a separator) and return the suggestion list for that substring.
P.S.
For the better user experience, you may also think of styling your AutoCompleteTextView contents with Spans: http://ballardhack.wordpress.com/2011/07/25/customizing-the-android-edittext-behavior-with-spans/
When looking at the Contact Groups on Google Contacts or in the People application of my HTC Legend phone, I get the groups names ok eg: Friends, Family, VIP, Favorite etc...
But in my application I get really wrong names such as
"Family" became "System Group: Family"
"Friends" became "System Group: Friends"
"Favorite" became "Favorite_5656100000000_3245664334564"
I use the below code to read these values:
public Cursor getFromSystem() {
// Get the base URI for the People table in the Contacts content
// provider.
Uri contacts = ContactsContract.Groups.CONTENT_URI;
// Make the query.
ContentResolver cr = ctx.getContentResolver();
// Form an array specifying which columns to return.
String[] projection = new String[] {
ContactsContract.Groups._ID, ContactsContract.Groups.TITLE,
ContactsContract.Groups.NOTES
};
Cursor managedCursor = cr.query(contacts, projection, ContactsContract.Groups.DELETED
+ "=0", null, ContactsContract.Groups.TITLE + " COLLATE LOCALIZED ASC");
return managedCursor;
}
What I am missing?
That sounds like a bug. One of my test phones has correct/sanitized titles, while the other has that type of incorrect title. I'd file this here.
I also inspected the contacts2.db database directly, and found that the SYSTEM_ID column seems to be sanitized – but that's probably not safe to use for display purposes.