I tried to get sms list and below code works well in some devices but not working in other devices. Details tested with four devices using below code.
LG optimus one[android 2.2] - works well. SS galaxy s3[android4.0.4] - works well. SS galaxy s2[android
2.3.5] - not working. SS galaxy s2[android 4.0.4] - not working.
It seems that result depends on devices not android version because two devices with same android version[4.0.4] show differently. Symptom in the devices not working is that c.getCount() = 0 even if they have many sms. The query returns nothing. Why these are? How can I get sms list in galaxy s2 as well?
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String smsMsgBody = null;
String smsMsgAddress = null;
String smsMsgDirection = null;
Uri uri = Uri.parse("content://sms");
// Uri uri = Uri.parse("content://sms/inbox");
// Uri uri = Uri.parse("content://sms/conversations/");
Cursor c= getContentResolver().query(uri, null, null,null,null);
// startManagingCursor(c);
if (c.getCount() > 0)
{
String count = Integer.toString(c.getCount());
while (c.moveToNext())
{
smsMsgBody = c.getString(c.getColumnIndex("body"));
smsMsgAddress = c.getString(c.getColumnIndex("address"));
smsMsgDirection = c.getString(c.getColumnIndex("type"));
// Do things using above sms data
}
}
c.close();
}
The content://sms/ is not a supported content provider. It is a hidden method, and may not be available in all devices. In Android 4.4 KitKat devices, you can use the code in this answer to do it:
public List<String> getAllSms() {
List<String> lstSms = new ArrayList<String>();
ContentResolver cr = mActivity.getContentResolver();
Cursor c = cr.query(Telephony.Sms.Inbox.CONTENT_URI, // Official CONTENT_URI from docs
new String[] { Telephony.Sms.Inbox.BODY }, // Select body text
null,
null,
Telephony.Sms.Inbox.DEFAULT_SORT_ORDER // Default sort order);
int totalSMS = c.getCount();
if (c.moveToFirst()) {
for (int i = 0; i < totalSMS; i++) {
lstSms.add(c.getString(0));
c.moveToNext();
}
}
else {
throw new RuntimeException("You have no SMS in Inbox");
}
c.close();
return lstSms;
}
I don't believe that there is a documented method that will work across all devices right now.
Related
How to read filenames from the download directory on android 10 and higher?
After filtering the names I want to let users select from this list to open
the file.
On Android versions 8 (API 28) the FILE api is quite simple to use.
Filenames are simple to read with the method DirListOld. With these names
I can read the content of the files.
I tried to make a method to do the same on Android 10 (API 30) and higher.
But documentation is not very clear. I did some experimenting with
MediaStore methods, but I could not get the filenames only got directory
names on external storage.
How to filter the results is not very well documented and examples of the
MediaStore.Downlaods are totally absent.
My experiment is shown in method DirListNew.
Also I had to ask for a permission for MANAGE_EXTERNAL_STORAGE. Without this
permission even DirListNew results in an empty string. As I read in several
comments Google-Play is not generous in giving this permission. Why not
special permission for only downloaded files. I don't have to read all external
files.
I don't understand why Google-Android developers made such a mess for retrieving
simple downloaded files.
public String DirListOld()
{
String sName;
File oDownloadDir;
String sDownloadDir;
StringBuilder dirContent = new StringBuilder();
oDownloadDir = this.getApplicationContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
try {
sDownloadDir = oDownloadDir.getName();
if (!sDownloadDir.equals("") )
{
for (File f : Objects.requireNonNull(oDownloadDir.listFiles()))
{
if (f.isFile())
{
sName = f.getName();
dirContent.append(sName);
dirContent.append("\n");
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return dirContent.toString();
} // DirListOld
#RequiresApi(api = Build.VERSION_CODES.Q)
public String DirListNew()
{
EditText editText = findViewById(R.id.editTextMultiLine2);
StringBuilder dirContent = new StringBuilder();
String[] projection = new String[] {
MediaStore.Downloads.DATA
};
String selection = null;
String[] selectionArgs = null;
String sortOrder = null;
Cursor cursor = getApplicationContext().getContentResolver().query(
MediaStore.Files.getContentUri("external"),
projection,
selection,
selectionArgs,
sortOrder
);
if (cursor != null) {
cursor.moveToFirst();
//iterate over rows
for (int i = 0; i < cursor.getCount(); i++) {
//iterate over the columns
for(int j = 0; j < cursor.getColumnNames().length; j++){
//append the column value to the string builder and delimit by \n
dirContent.append(cursor.getString(j));
dirContent.append("\n");
}
//add a new line carriage return
dirContent.append("\n");
//move to the next row
cursor.moveToNext();
}
//close the cursor
cursor.close();
}
return dirContent.toString();
} // DirListNew
I want to retrieve a user's profile and it's image, but this is not working. I always get an empty cursor (cursor.getCount() == 0). Can someone help?
I have a profile with an image and a phone number on my phone but I can't read it. Permissions (read and write contacts permissions) are granted and I can retrieve all my phone contacts, but not the own profile.
Any ideas?
Code
void loadUser() {
Uri dataUri = Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI, ContactsContract.Contacts.Data.CONTENT_DIRECTORY);
String[] selection = new String[]
{
ContactsContract.Data.RAW_CONTACT_ID,
ContactsContract.Data._ID,
ContactsContract.Profile.DISPLAY_NAME,
ContactsContract.Profile.PHOTO_URI,
ContactsContract.Profile.LOOKUP_KEY,
ContactsContract.Data.DATA_VERSION
};
Cursor cursor = MainApp.get().getContentResolver().query(
dataUri,
selection,
null,
null,
null);
if (cursor != null) {
L.d("MY PROFILE - cursor size: %d", cursor.getCount());
int rawId = cursor.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID);
int id = cursor.getColumnIndex(ContactsContract.Data._ID);
int name = cursor.getColumnIndex(ContactsContract.Profile.DISPLAY_NAME);
int photoUri = cursor.getColumnIndex(ContactsContract.Profile.PHOTO_URI);
int lookupKey = cursor.getColumnIndex(ContactsContract.Profile.LOOKUP_KEY);
int version = cursor.getColumnIndex(ContactsContract.Data.DATA_VERSION);
try {
if (cursor.moveToFirst()) {
long phRawId = cursor.getLong(rawId);
int phId = cursor.getInt(id);
String phName = cursor.getString(name);
String phImageUri = cursor.getString(photoUri);
String phLookupKey = cursor.getString(lookupKey);
int phVersion = cursor.getInt(version);
boolean phExists = true;
L.d("MY PROFILE - RawID: %d, ID: %d", phRawId, phId);
// ... profile successfully retrieved
} else {
L.d("MY PROFILE - cursor is EMPTY");
}
} finally {
cursor.close();
}
} else {
L.d("MY PROFILE - cursor = NULL");
}
}
Additional info
I think this code worked on my S6 with android 7 but it's not working on my new S9 with android 8 on it (can't test it on my old phone anymore as it's not working anymore). So this may be an android version specific problem...
This appears to be bad implementation of Samsung's Contacts app, I've opened a bug report on their developer's forum here: https://developer.samsung.com/forum/thread/contacts-app-profile-is-not-accessible-via-contactscontractprofile-api/201/354874
I am developing an android app which backs up and restores the messages/conversations from device. It backup the messages, export file in the form of xml, and then later restore it. The only problem I am facing is the date/times of conversations. It is changed to current time at the time of restoration, but when I open any conversation, there time is correct. Have a look at photos.
Before backup:
After backup:
Code I am using for backup:
Uri uri = Uri.parse("content://sms/inbox");
//Uri uri = Uri.parse("content://mms-sms/conversations/");
ContentResolver contentResolver = getContentResolver();
final String[] projection = new String[]{"*"};
Cursor SMSL = contentResolver.query(Telephony.Sms.Inbox.CONTENT_URI, projection, null, null, null);
int msgscount = SMSL.getCount();
if (msgscount>0) {
msgs = new String[SMSL.getCount()][5];
int i = 0;
while (SMSL.moveToNext()) {
address = SMSL.getString(SMSL.getColumnIndex("address"));
body = SMSL.getString(SMSL.getColumnIndex("body"));
read = SMSL.getString(SMSL.getColumnIndex("read"));
date = SMSL.getString(SMSL.getColumnIndex("date"));
type = SMSL.getString(SMSL.getColumnIndex("type"));
msgs[i][0] = address;
msgs[i][1] = body;
msgs[i][2] = date;
msgs[i][3] = read;
msgs[i][4] = type;
Log.i("Date: ", String.valueOf(SMSL.getLong(SMSL.getColumnIndex("date"))));
i++;
}
SMSL.close();
}else{
msgs = new String[0][0];
Toast.makeText(getApplicationContext(),"No messages found!",Toast.LENGTH_LONG).show();
}
Code for restoring:
ContentResolver contentResolver = getContentResolver();
Uri uri = Uri.parse("content://sms/inbox");
//Uri uri = Uri.parse("content://mms-sms/conversations/");
ContentValues values = new ContentValues();
for (int i = 0; i < readMsgsFromFile.length; i++) {
values.put("address",readMsgsFromFile[i][0]);
values.put("body",readMsgsFromFile[i][1]);
values.put("date",readMsgsFromFile[i][2]);
values.put("read",readMsgsFromFile[i][3]);
values.put("type",readMsgsFromFile[i][4]);
contentResolver.insert(Telephony.Sms.Inbox.CONTENT_URI, values);
Log.i("Restoring: ",readMsgsFromFile[i][2]);
}
Thanks Mike M. I did find a solution and you are right, the conversation table is updated whenever a new message is received or sent by a user and the time of conversation is same as that message's (whether received or sent) time. But in case of writing messages through contentresolver query it does not work and the conversation time is current time at the time of writing. So what I did is add a temporary message in all of the conversations, right after messages are restored. And after that delete all the temporary messages, this will update the conversations time to last message time.
HashSet hs = new HashSet();
hs.addAll(list);
//list is the ArrayList<String> which contains the addressess of all the messages and
//through hashset we remove all the duplicates to get only the addressess once and hence we know the number of conversations and their addressess.
list.clear();
list.addAll(hs);
//Add some dummy message to each conversation
ContentValues values2 = new ContentValues();
for (int i = 0; i < list.size(); i++) {
values2.put("address",list.get(i));
values2.put("date_sent",readMsgsFromFile[0][1]);
values2.put("date",readMsgsFromFile[0][2]);
values2.put("type",readMsgsFromFile[0][3]);
values2.put("body","temp"); //this should be more unique
values2.put("read",readMsgsFromFile[0][5]);
values2.put("service_center","01010101");
contentResolver.insert(Telephony.Sms.CONTENT_URI, values2);
}
//Now deleting that message with body 'temp' from each conversation
Cursor c = contentResolver.query(Telephony.Sms.CONTENT_URI,null,null,null,null);
while (c.moveToNext()){
String body = c.getString(c.getColumnIndex("body"));
String mid = c.getString(0);
if (body.equals("temp")){
Log.i("Deleting ",mid);
getContentResolver().delete(Uri.parse(Telephony.Sms.CONTENT_URI+"/"+mid),null,null);
}
}
c.close();
This word 'temp' could be and should be more unique so that it is not mixed with actual message.
I am trying to fetch all sms(both inbox,sent sms) in android.
I have tried with these
Uri uri_sent = Uri.parse("content://sms");
Cursor c_sent = getContentResolver().query(uri_sent, null, null, null,
null);
startManagingCursor(c_sent);
// Read the inbox sms data and store it in the list
if (c_sent.moveToFirst()) {
for (int i = 0; i < c_sent.getCount(); i++) {
SmsDataClass sms = new SmsDataClass();
sms.setAddress(c_sent.getString(
c_sent.getColumnIndexOrThrow("address")).toString());
sms.setBody(c_sent.getString(
c_sent.getColumnIndexOrThrow("body")).toString());
sms.setDate(c_sent.getString(
c_sent.getColumnIndexOrThrow("date")).toString());
sms.set_id(c_sent
.getString(c_sent.getColumnIndexOrThrow("_id"))
.toString());
sms.setType(c_sent.getString(
c_sent.getColumnIndexOrThrow("type")).toString());
// sms.setTitle(c.getString(c.getColumnIndexOrThrow("title")).toString());
all_sms_data.add(sms);
c_sent.moveToNext();
}
} else
c_sent.close();
it sometimes works but it doesn't work regularly. Sometimes it shows null pointer exception on c_sent.getColumnIndexOrThrow("address"),c_sent.getColumnIndexOrThrow("body") ....
What is the problem ???
I dont understand.
Before oneday it works fine when today i have nullpointer exception.
Why???
I want to load several contacts via Xamarin.Contacts.AddressBook, at the moment I have something like:
var loookupIDs = /* load 10 saved contact IDs */
var addressBook = new AddressBook(context) { PreferContactAggregation = true };
foreach(var id in loookupIDs)
{
var contact = addressBook.Load(id);
names.Add(contact.DisplayName);
}
However, this is really slow (tested on Android device) - even just loading 10 contacts. Is there a way to batch up the loading so it's faster? Or is the only option to use platform specific APIs instead of the Xamarin wrapper.
Yes, Xamarin.Mobile is kind of slow. It combines all possible contacts (phones, mails, etc) and all possible fields, which is not recommended by Android reference manual.
I recommend you to use native way to query your contacts with Cursor and filter it for your needs. Sadly, Xamarin dev mixed up all constants, so it is not trivial task.
Here is complete example
public class PhoneContactInfo
{
public string PhoneContactID { get; set; }
public string ContactName { get; set; }
public string ContactNumber { get; set; }
}
public IEnumerable<PhoneContactInfo> GetAllPhoneContacts(IEnumerable<int> filterIds = null)
{
Log.Debug("GetAllPhoneContacts", "Getting all Contacts");
var arrContacts = new System.Collections.Generic.List<PhoneContactInfo>();
PhoneContactInfo phoneContactInfo = null;
var uri = ContactsContract.CommonDataKinds.Phone.ContentUri;
string[] projection = { ContactsContract.Contacts.InterfaceConsts.Id,
ContactsContract.Contacts.InterfaceConsts.DisplayName,
ContactsContract.CommonDataKinds.Phone.Number
};
//String[] strings = filterIds.Select(k => Convert.ToString(k)).ToArray();
//string whereClause = ContactsContract.Contacts.InterfaceConsts.Id + " = ? ";
var cursor = MainActivity.ContextHolder.ContentResolver.Query(uri, projection,
null,
null,
null);
cursor.MoveToFirst();
while (cursor.IsAfterLast == false)
{
int phoneContactID = cursor.GetInt(cursor.GetColumnIndex(ContactsContract.Contacts.InterfaceConsts.Id));
if (filterIds.Contains(phoneContactID))
{
String contactNumber = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.Phone.Number));
String contactName = cursor.GetString(cursor.GetColumnIndex(ContactsContract.Contacts.InterfaceConsts.DisplayName));
phoneContactInfo = new PhoneContactInfo()
{
PhoneContactID = Convert.ToString(phoneContactID),
ContactName = contactName,
ContactNumber = contactNumber
};
arrContacts.Add(phoneContactInfo);
}
cursor.MoveToNext();
}
cursor.Close();
cursor = null;
Log.Debug("GetAllPhoneContacts", "Got all Contacts");
return arrContacts;
}
If you wish to add some fancy async
public Task<IEnumerable<PhoneContactInfo>> GetAllPhoneContactsAsync(IEnumerable<int> filterIds)
{
return Task.FromResult(GetAllPhoneContacts(filterIds));
}
Also take a look at commented whereClause. You possibly can construct 'SQL like' where clause to make this query even more faster. Just build a string with several '=' and 'or'
P.S.
I didn't measure performance differences, if anyone has decent statistics i will be grateful
It looks like you access AdressBook for each loookupID, this might cause your speed issue.
Try:
1) Fetch all contacts, or only those you might be interested in. (Use Linq)
2) Do further work with found contacts
Example from Xamarin docs:
http://blog.xamarin.com/introducing-xamarin-contacts/
var book = new AddressBook (this) {
PreferContactAggregation = true
};
foreach (Contact c in book.Where (c => c.LastName == "Smith")) {
print (c.DisplayName);
foreach (Phone p in c.Phones)
print ("Phone: " + p.Number);
foreach (Email e in c.Emails)
print ("Email: " + e.Address);
}