I develop simple sms/mms client. With sms everything work good, but with mms I have problem related to address field in conversation.
I have next method to load conversations. There are last sms and mms.
public static List<Conversation> getConversations(Context c) {
List<Conversation> conversations = new ArrayList<>();
Conversation conversation;
Uri uri = Uri.parse("content://mms-sms/conversations/");
Cursor cursor = c.getContentResolver().query(uri, null, null, null, "normalized_date DESC");
if (cursor.moveToFirst()) {
for (int i = 0; i < cursor.getCount(); i++) {
conversation = new Conversation();
conversation.setId(cursor.getString(cursor.getColumnIndexOrThrow("_id")));
conversation.setThreadID(cursor.getString(cursor.getColumnIndexOrThrow("thread_id")));
conversation.setDate(new Date(Long.valueOf(cursor.getString(cursor.getColumnIndexOrThrow("date")))));
conversation.setReadType(ReadType.values()[Integer.parseInt(cursor.getString(cursor.getColumnIndexOrThrow("read")))]);
String type = cursor.getString(cursor.getColumnIndexOrThrow("type"));
if (isSMS(type)) {
conversation.setBody(cursor.getString(cursor.getColumnIndexOrThrow("body")));
conversation.setNumber(cursor.getString(cursor.getColumnIndexOrThrow("address")));
conversation.setMessageFromType(MessageFromType.values()[Integer.parseInt(type) - 1]);
} else {
Map<String, String> mmsContent = getMMSByID(c, conversation.getId());
conversation.setBody(mmsContent.get("body"));
conversation.setNumber(mmsContent.get("address"));
}
conversations.add(conversation);
cursor.moveToNext();
}
}
cursor.close();
return conversations;
}
The problem is that when I sent mms, my number was put to address field of conversation. With sms everything is okay. So after that I don't know who is my opponent in chat.
Also I load mms number in the next way
private String getNumber(Context c, String mmsdid) {
String add = "";
final String[] projection = new String[]{"address", "contact_id"};
Uri.Builder builder = Uri.parse("content://mms").buildUpon();
builder.appendPath(String.valueOf(mmsid)).appendPath("addr");
Cursor cursor = c.getContentResolver().query(
builder.build(),
projection,
null,
null, null);
if (cursor.moveToFirst()) {
add = cursor.getString(cursor.getColumnIndex("address"));
}
return add;
}
Maybe someone have the same problem? Or have any suggestions how to solve it?
The answer was pretty easy, hope it may help someone. Everything is okay except one part. In selection parameter I should add
"from=151"
or
"from="+PduHeaders.TO
So code will look like this:
private static String getNumber(Context c, String id) {
String add = "";
final String[] projection = new String[]{"address"};
Uri.Builder builder = Uri.parse("content://mms").buildUpon();
builder.appendPath(String.valueOf(id)).appendPath("addr");
Cursor cursor = c.getContentResolver().query(
builder.build(),
projection,
"type="+PduHeaders.TO,
null, null);
if (cursor.moveToFirst()) {
add = cursor.getString(cursor.getColumnIndex("address"));
}
cursor.close();
return add;
}
Related
I am trying to do fuzzy search of contacts in my android application with partial phone number, with below code. But my search function always ends in no result.
For example,
I have contact with phone number 1234567890 with name as "example".
fuzzySearch("4567"); should have return with contact named "example".
Can some please point me where am I wrong? I have checked here. But ended in run-time sql query error.
public ArrayList<Contact> fuzzySearch(String match) {
private static final String SELECTION = Phone.NUMBER + " LIKE ? COLLATE NOCASE";
Uri uri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, Uri.encode(match));
String[] projectionFields = new String[]{ContactsContract.Contacts._ID, Phone.NUMBER};
CursorLoader cursorLoader = null;
ArrayList<Contact> listContacts = new ArrayList<>();
if(match.trim().length() == 0){
return listContacts;
}else {
cursorLoader = new CursorLoader(context, uri, projectionFields, SELECTION, new String[]{"%"+match+"%"}, Phone.NUMBER);
}
Cursor c = cursorLoader.loadInBackground();
final Map<String, Contact> contactsMap = new HashMap<>(c.getCount());
if (c.moveToFirst()) {
int idIndex = c.getColumnIndex(ContactsContract.Contacts._ID);
int nameIndex = c.getColumnIndex(Phone.NUMBER);
do {
String contactId = c.getString(idIndex);
String contactDisplayName = c.getString(nameIndex);
Contact contact = new Contact(contactId, contactDisplayName);
contactsMap.put(contactId, contact);
listContacts.add(contact);
} while (c.moveToNext());
}
c.close();
return listContacts;
}
use PhoneLookup instead of phone.
because you already encode the search key into the URL, you don't need SELECTION, remove it.
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(match));
Cursor cur = resolver.query(uri, new String[]{PhoneLookup.DISPLAY_NAME,PhoneLookup.NUMBER, PhoneLookup.CONTACT_ID}, null, null, null);
I'm working on a Cordova app that needs to be able to get a list of phone numbers involved in a group text. I'm querying content://mms/[id]/addr for that. I'm testing on a Pixel 2 and for the MMS messages prior to March 10, 2018, this works fine. But for messages on or after that date, it fails (comes back as null). Is there a different address I should be querying? Any other ideas?
Using content://mms/ wil give you MMS conversation list and using content://mms-sms/conversations will give you both the first one or 2nd one you can try both if any of them doesn't work
so first you will have to get a list of MMS only using
ContentResolver contentResolver = getContentResolver();
final String[] projection = new String[]{"_id", "ct_t"};
Uri uri = Uri.parse("content://mms-sms/conversations");
Cursor query = contentResolver.query(uri, projection, null, null, null);
if (query.moveToFirst()) {
do {
String itemId = query.getString(query.getColumnIndex("_id"));
int getRowID = Integer.parseInt(itemId);
String string = query.getString(query.getColumnIndex("ct_t"));
if ("application/vnd.wap.multipart.related".equals(string)) {
// this item is MMS so get the number using function getAddressNumber and log it
Log.d("number","address/number:" + getAddressNumber(getRowID));
} else {
// item is sms do nothing
}
} while (query.moveToNext());
}
private String getAddressNumber(int id) {
String selectionAdd = new String("msg_id=" + id);
String uriStr = MessageFormat.format("content://mms/{0}/addr", id);
Uri uriAddress = Uri.parse(uriStr);
Cursor cAdd = getContentResolver().query(uriAddress, null,
selectionAdd, null, null);
String name = null;
if (cAdd.moveToFirst()) {
do {
String number = cAdd.getString(cAdd.getColumnIndex("address"));
if (number != null) {
try {
Long.parseLong(number.replace("-", ""));
name = number;
} catch (NumberFormatException nfe) {
if (name == null) {
name = number;
}
}
}
} while (cAdd.moveToNext());
}
if (cAdd != null) {
cAdd.close();
}
return name;
}
if above function getAddressNumber doesn't work you can try this one as well with a little bit of changes
public static String getMMSAddress(Context context, String id) {
String addrSelection = "type=137 AND msg_id=" + id;
String uriStr = MessageFormat.format("content://mms/{0}/addr", id);
Uri uriAddress = Uri.parse(uriStr);
String[] columns = { "address" };
Cursor cursor = context.getContentResolver().query(uriAddress, columns,
addrSelection, null, null);
String address = "";
String val;
if (cursor.moveToFirst()) {
do {
val = cursor.getString(cursor.getColumnIndex("address"));
if (val != null) {
address = val;
break;
}
} while (cursor.moveToNext());
}
if (cursor != null) {
cursor.close();
}
return address;
}
here is the defination for line
String addrSelection = "type=137 AND msg_id=" + id;
type constant come from the PduHeadersPduHeaders
class: 0x97 / 151 is PduHeaders.TO and 0x89 / 137 is PduHeaders.FROM you can change FROM or TO what you need.
if its still empty try below part and implement this in your code
Uri uriMms = Uri.parse("content://mms/");
final String[] projection = new String[]{"*"};
Cursor cursor = contentResolver.query(uriMms, projection, null, null, null);
String id = cursor.getString(cursor.getColumnIndex("_id"));
String selectionPart = "mid=" + id;
Uri uri = Uri.parse("content://mms/part");
Cursor cursor2 = getContentResolver().query(uri, null, selectionPart, null, null);
Here's how AOSP's (Android Open Source Project) messaging app does it:
Every message (SMS/ MMS) has a message ID represented as _ID in their respective tables
With this id pull the thread for the respective message
The threads table has a column called recipient_ids, this might be Space separated for group message, something like this:
123 456 789
Where 123 456 etc are the recipient ids.
Get address for respective recipient ids. Now this is a bit tricky, but AOSP uses the following uri: content://mms-sms/canonical-address
So the final method to get an array of addresses looks something likes this:
private fun getAddressFromRecipientId(spaceSepIds: String, context: Context): Array<String?> {
val singleCanonicalAddressUri = Uri.parse("content://mms-sms/canonical-address")
with(spaceSepIds.split(" ")) {
val addressArray: Array<String?> = arrayOfNulls(this.size)
this.forEachIndexed { index, address ->
if (!address.isEmpty()) {
val longId = address.toLong()
context.contentResolver.query(ContentUris.withAppendedId(singleCanonicalAddressUri, longId), null, null, null, null).use { cursor ->
if (cursor != null && cursor.moveToNext())
addressArray[index] = "${cursor.getString(0)} "
}
}
}
return addressArray
}
return arrayOf()
}
Hope this helps. Also the function is in kotlin but it's pretty easy to figure out what's happening there.
Also, you already have ids, so you can just call this method with either the space separated IDs or without them, the function works both ways.
I am trying to read latest SMS from All contacts but the procedure Currently I am using seems to be very slow. How can I improve it?
public Map<String, SMS> getLatestMsgs(){
Uri uriSMSURI = Uri.parse("content://sms/");
String[] projection = {_id, address, body,
read, date, subscriptionId, type};
String selection = "";
String[] selectionArgs = {};
String sortOrder = Telephony.Sms.DATE +" desc";
Cursor c = context.getContentResolver()
.query(uriSMSURI, projection, selection, selectionArgs, sortOrder);
LinkedHashMap<String, SMS> smsMap = new LinkedHashMap<>();
log.info(methodName, "Reading Messages..");
if(c==null){
log.info(methodName, "Query returned null cursor");
return smsMap;
}
while (c.moveToNext()) {
String id = c.getString(c.getColumnIndexOrThrow(this._id));
String from = c.getString(c.getColumnIndexOrThrow(this.address));
String body = c.getString(c.getColumnIndexOrThrow(this.body));
int serviceCenter = c.getInt(c.getColumnIndexOrThrow(this.subscriptionId));
boolean readState = c.getInt(c.getColumnIndex(this.read)) == 1;
long time = c.getLong(c.getColumnIndexOrThrow(this.date));
long type = c.getLong(c.getColumnIndexOrThrow(this.type));
if(!smsMap.containsKey(from)) {
SMS sms = new SMS();
sms.setId(id);
sms.setFrom(from);
sms.setBody(body);
sms.setRead(readState);
sms.setDateTime(time);
sms.setType(type);
sms.setSubscription(serviceCenter);
smsMap.put(from, sms);
}
}
c.close();
return smsMap;
}
This method has following steps:
Read all sms in decreasing order of timestamp
Start Iterating over fetched rows
If sms address already in map then ignore
else put SMS into LinkedHashMap
Am trying to use the ContactsProvider with my AutoCompleteTextview using a method that fetches the data (name and phone number) and stores them in a list. As expected, this method will always take time to complete as am calling the method in the onCreateView method of my Fragment class.
This is the method:
...
ArrayList<String> phoneValues;
ArrayList<String> nameValues;
...
private void readContactData() {
try {
/*********** Reading Contacts Name And Number **********/
String phoneNumber = "";
ContentResolver contentResolver = getActivity()
.getContentResolver();
//Query to get contact name
Cursor cursor = contentResolver
.query(ContactsContract.Contacts.CONTENT_URI,
null,
null,
null,
null);
// If data data found in contacts
if (cursor.getCount() > 0) {
int k=0;
String name = "";
while (cursor.moveToNext())
{
String id = cursor
.getString(cursor
.getColumnIndex(ContactsContract.Contacts._ID));
name = cursor
.getString(cursor
.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
//Check contact have phone number
if (Integer
.parseInt(cursor
.getString(cursor
.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0)
{
//Create query to get phone number by contact id
Cursor pCur = contentResolver
.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID
+ " = ?",
new String[] { id },
null);
int j=0;
while (pCur
.moveToNext())
{
// Sometimes get multiple data
if(j==0)
{
// Get Phone number
phoneNumber =""+pCur.getString(pCur
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
// Add contacts names to adapter
autocompleteAdapter.add(name);
// Add ArrayList names to adapter
phoneValues.add(phoneNumber.toString());
nameValues.add(name.toString());
j++;
k++;
}
} // End while loop
pCur.close();
} // End if
} // End while loop
} // End Cursor value check
cursor.close();
} catch (Exception e) {
Log.i("AutocompleteContacts","Exception : "+ e);
}
}
Am sure there is a better way to accomplish this, but this method works and the suggestions are presented when I type into the AutocompleteTextview. Am just worried about the time it takes. How can I accomplish this without populating an ArrayList?
I have looked at this question: Getting name and email from contact list is very slow and applied the suggestions in the answer to my code, but now nothing is suggested when I type.How can I improve the performance of my current code?
This is how i have implemented AutoCompleteTextView and it works fine for me ..
final AutoCompleteTextView act=(AutoCompleteTextView)findViewById(R.id.autoCompleteTextView1);
ArrayList<String> alContacts = new ArrayList<String>();
ArrayList<String> alNames= new ArrayList<String>();
ContentResolver cr = this.getContentResolver(); //Activity/Application android.content.Context
Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if(cursor.moveToFirst())
{
do
{
String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
if(Integer.parseInt(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0)
{
Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",new String[]{ id }, null);
while (pCur.moveToNext())
{
String contactNumber = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
String contactName=pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
alNames.add(contactName);
alContacts.add(contactNumber);
break;
}
pCur.close();
}
} while (cursor.moveToNext()) ;
}
String[] array=new String[alNames.size()];
array=(String[]) alNames.toArray(array);
ArrayAdapter<String> myArr= new ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line,array);
act.setAdapter(myArr);
act.setThreshold(1);
I got rid of the slow response by placing the method inside an AsynTask.
public class AutocompleteAsyncTask extends AsyncTask<Void, Void, Void> {
public Void doInBackground(Void...params) {
try {
/*********** Reading Contacts Name And Number **********/
String phoneNumber = "";
ContentResolver contentResolver = getActivity()
.getContentResolver();
//Query to get contact name
Cursor cursor = contentResolver
.query(ContactsContract.Contacts.CONTENT_URI,
null,
null,
null,
null);
// If data data found in contacts
if (cursor.getCount() > 0) {
int k=0;
String name = "";
while (cursor.moveToNext())
{
//...Rest of the same code as above
and then calling this in my onCreateView():
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
new AutocompleteAsyncTask().execute();
return rootView;
}
Now the task of inflating my view and fetching the data are separated into two different threads.
The CursorLoader option mentioned by Eugen Pechanec is kinda the best option, so I'll update this answer with that option when I can.
In my application I'm able to get all MMS through code below, but I am not able to get the mms address. Can you guys/girls please help me?
Cursor cursor = activity.getContentResolver().query(Uri.parse("content://mms"),null,null,null,date DESC);
count = cursor.getCount();
if (count > 0) {
cursor.moveToFirst();
long messageId = cursor.getLong(0);
long threadId = cursor.getLong(1);
long timestamp = cursor.getLong(2);
String subject = cursor.getString(3);
}
to get address from MMS. do something like this
in msgnumber pass your messageID.
String add="";
final String[] projection = new String[] { "address", "contact_id", "charset", "type" };
final String selection = "type=137"; // "type="+ PduHeaders.FROM,
Uri.Builder builder = Uri.parse("content://mms").buildUpon();
builder.appendPath(String.valueOf(msgnumber)).appendPath("addr");
Cursor cursor = context.getContentResolver().query(
builder.build(),
projection,
selection,
null, null);
if (cursor.moveToFirst()) {
add = cursor.getString(0);
}
Hope this will help.