Strange exception in creating Android Contacts Group - android

looking at my google analytics page lately, I have seen that sometimes an exception is raised and I have no clue to resolve it as I have not been able to reproduce it. The idea of the code is to check if a custom contacts group exists and if not, create it. The code is the following:
public static long ensureSampleGroupExists(Context context, Account account) {
Log.v(TAG, "ensureSampleGroupExists");
final ContentResolver resolver = context.getContentResolver();
// Lookup the sample group
long groupId = 0;
final Cursor cursor = resolver.query(Groups.CONTENT_URI,
new String[] { Groups._ID },
Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE
+ "=? AND " + Groups.TITLE + "=?", new String[] {
account.name, account.type, SAMPLE_GROUP_NAME }, null);
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
groupId = cursor.getLong(0);
}
} finally {
cursor.close();
}
}
if (groupId == 0) {
// Sample group doesn't exist yet, so create it
final ContentValues contentValues = new ContentValues();
contentValues.put(Groups.ACCOUNT_NAME, account.name);
contentValues.put(Groups.ACCOUNT_TYPE, account.type);
contentValues.put(Groups.TITLE, SAMPLE_GROUP_NAME);
contentValues.put(Groups.GROUP_IS_READ_ONLY, true);
final Uri newGroupUri = resolver.insert(Groups.CONTENT_URI,
contentValues); //<-- NULLPOINTER EXCEPTION IN SOME DEVICES (or at least 1 :P)
groupId = ContentUris.parseId(newGroupUri);
}
return groupId;
}
I can see in the google analytics webpage the following exception --
NullPointerException (#ContactManager:ensureSampleGroupExists:95) {SyncAdapterThread-1}
Not sure if the problem is the ContentResolver but when checked it was not null (neither was null the context) so i am a little bit confused...
Unfortunately there is no bug report so I have no stack trace:(, does somebody have any clue about what could be happening? Any help would be highly appreciated
Thank you
EDIT
I managed to get the full stacktrace thanks to GA 4, check it out
java.lang.NullPointerException at
android.os.Parcel.readException(Parcel.java:1478)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:185)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:137)
at android.content.ContentProviderProxy.insert(ContentProviderNative.java:468)
at android.content.ContentResolver.insert(ContentResolver.java:1190)
at com.smm.epctrackerapp.sqlite.ContactManager.ensureSampleGroupExists(ContactManager.java:96)
at com.smm.epctrackerapp.adapter.ContactSyncAdapter.onPerformSync(ContactSyncAdapter.java:72)
at android.content.AbstractThreadedSyncAdapter$SyncThread.run(AbstractThreadedSyncAdapter.java:259)

Related

Android Cursor Illegal state exception crash

I am having some issues with a piece of android code, the crash seems to be occurring here c.moveToFirst(). I have tried to get a count, and if the cursor result is greater than 0; then execute that code, but that doesn't help.
I am unable to reproduce this error on my apps (I have tried several scenarios), However, through Google Dev Console, I am seeing that other people that are using my app are getting this error.
Thanks in advance
Crash error:
java.lang.IllegalStateException:
at android.database.sqlite.SQLiteConnectionPool.throwIfClosedLocked (SQLiteConnectionPool.java:1198)
at android.database.sqlite.SQLiteConnectionPool.waitForConnection (SQLiteConnectionPool.java:808)
at android.database.sqlite.SQLiteConnectionPool.acquireConnection (SQLiteConnectionPool.java:534)
at android.database.sqlite.SQLiteSession.acquireConnection (SQLiteSession.java:904)
at android.database.sqlite.SQLiteSession.executeForCursorWindow (SQLiteSession.java:834)
at android.database.sqlite.SQLiteQuery.fillWindow (SQLiteQuery.java:62)
at android.database.sqlite.SQLiteCursor.fillWindow (SQLiteCursor.java:152)
at android.database.sqlite.SQLiteCursor.getCount (SQLiteCursor.java:141)
at com.andre3.wzlimitdistraction.dao.Dao.read (Dao.java:101)
at com.andre3.wzlimitdistraction.utils.MonitorService$2.run (MonitorService.java:201)
at java.util.TimerThread.mainLoop (Timer.java:555)
at java.util.TimerThread.run (Timer.java:505)
Code thats causing it:
ArrayList<UserApps> arr = new ArrayList<UserApps>();
int profileId = 0;
SQLiteDatabase getDb = db.getReadableDatabase();
if(getDb.isOpen()) {
String[] columns = {Dbinfo.app.toString(), Dbinfo.app_name.toString(), Dbinfo.start_duration.toString(), Dbinfo.active_duration.toString(), Dbinfo.start_time.toString(), Dbinfo.profile_id.toString()};
String selection = Dbinfo.app.toString() + " =? and " + Dbinfo.profile_id.toString() + " <=?";
String[] selectionArgs = {id, String.valueOf(profileId)};
Cursor c = getDb.query("user_apps", columns, selection, selectionArgs, null, null, null);
if((c.getCount() > 0) && (c !=null)) {
if (c.moveToFirst()) {
do {
UserApps form = new UserApps();
form.setPkg(c.getString(c.getColumnIndex(Dbinfo.app.toString())));
form.setName(c.getString(c.getColumnIndex(Dbinfo.app_name.toString())));
arr.add(form);
} while (c.moveToNext());
}
}
getDb.close();
c.close();
}
return arr;
This is possibly because getColumnIndex can not find the column you specified . You can Refer this Question

How to query all contacts that belong to an account?

Background
Contacts on the address book can have an account data that's attached to them.
Each app can have an account, and then add its own information for the contact.
Apps such as Telegram, WhatsApp, Viber,... - all create an account that adds information and/or actions to contacts.
Here's an example of a contact that has both WhatsApp and Viber accounts for it:
The problem
I'm trying to figure out how to fetch all contacts that have a specified account.
Since WhatsApp is the most popular that I know of, my tests focus on it.
My problem is that some users claim what I did barely returns contacts, and some claim it doesn't show even a single one. It seems to usually work, and in my case it always worked, but something is probably not good on the code.
What I've tried
I got to make the next code, which to me seems to work, getting a map of phone-to-contact-info, of all WhatsApp contacts.
The idea is to get all possible information of WhatsApp contacts, vs all basic contacts data, and merge those that match the same lookup-key.
I tried to use a better query of joining, but I failed. Maybe it is possible too, and might be more efficient.
Here's the code:
/**
* returns a map of lookup-key to contact-info, of all WhatsApp contacts
*/
#NonNull
public HashMap<String, ContactInfo> getAllWhatsAppPhones(Context context) {
ContentResolver cr = context.getContentResolver();
final HashMap<String, ContactInfo> phoneToContactInfoMap = new HashMap<>();
final HashMap<String, String> whatsAppLookupKeyToPhoneMap = new HashMap<>();
final String phoneMimeType = Phone.CONTENT_ITEM_TYPE;
final Cursor whatsAppCursor;
whatsAppCursor = cr.query(Data.CONTENT_URI,
new String[]{Phone.NUMBER, Phone.LOOKUP_KEY},
Phone.MIMETYPE + " = ?", new String[]{WhatsAppStuff.WHATS_APP_MIME_TYPE}, null);
if (whatsAppCursor == null)
return phoneToContactInfoMap;
Cursor contactCursor = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
//null,
new String[]{
Contacts.LOOKUP_KEY, Contacts._ID, Contacts.PHOTO_THUMBNAIL_URI,
ContactsContract.Contacts.DISPLAY_NAME, // ContactsContract.CommonDataKinds.Phone.NUMBER,
},
"(" + Phone.MIMETYPE + " IS NULL OR " + Phone.MIMETYPE + " = '" + phoneMimeType + "') AND ("
+ ContactsContract.RawContacts.ACCOUNT_TYPE + " = 'com.google' OR " + ContactsContract.RawContacts.ACCOUNT_TYPE + " IS NULL)",
null, null);
if (contactCursor == null) {
whatsAppCursor.close();
return phoneToContactInfoMap;
}
int progress = 0;
final int phoneNumberIdx = whatsAppCursor.getColumnIndex(Phone.NUMBER);
final int lookupKeyIdx = whatsAppCursor.getColumnIndex(Phone.LOOKUP_KEY);
while (whatsAppCursor.moveToNext()) {
final String phoneNumberValue = whatsAppCursor.getString(phoneNumberIdx);
final int endIndex = phoneNumberValue.indexOf("#");
if (endIndex < 0)
continue;
String lookupKey = whatsAppCursor.getString(lookupKeyIdx);
final String phone = phoneNumberValue.substring(0, endIndex);
if (!phone.isEmpty() && StringUtil.isAllDigits(phone)) {
//Log.d("AppLog", "whatsApp phone:" + phone + " " + lookupKey);
whatsAppLookupKeyToPhoneMap.put(lookupKey, phone);
}
if (markedToCancel != null && markedToCancel.get()) {
whatsAppCursor.close();
contactCursor.close();
return phoneToContactInfoMap;
}
if (progressListener != null)
progressListener.onProgressUpdate(progress++, maxProgress);
}
whatsAppCursor.close();
if (whatsAppLookupKeyToPhoneMap.isEmpty())
return phoneToContactInfoMap;
//Log.d("AppLog", "getting info about whatsapp contacts");
final int idColIdx = contactCursor.getColumnIndex(Contacts._ID);
final int displayNameColIdx = contactCursor.getColumnIndex(Contacts.DISPLAY_NAME);
final int lookupKeyColIdx = contactCursor.getColumnIndex(Contacts.LOOKUP_KEY);
final int photoColIdx = contactCursor.getColumnIndex(Contacts.PHOTO_THUMBNAIL_URI);
while (contactCursor.moveToNext()) {
String lookupKey = contactCursor.getString(lookupKeyColIdx);
String phoneNumber = whatsAppLookupKeyToPhoneMap.get(lookupKey);
if (phoneNumber == null)
continue;
ContactInfo contactInfo = new ContactInfo();
contactInfo.lookupKey = lookupKey;
contactInfo.displayName = contactCursor.getString(displayNameColIdx);
contactInfo.photoThumbUriStr = contactCursor.getString(photoColIdx);
contactInfo.whatsAppPhoneNumber = phoneNumber;
contactInfo.contactId = contactCursor.getLong(idColIdx);
phoneToContactInfoMap.put(phoneNumber, contactInfo);
if (markedToCancel != null && markedToCancel.get()) {
contactCursor.close();
return phoneToContactInfoMap;
}
if (progressListener != null)
progressListener.onProgressUpdate(progress++, maxProgress);
}
contactCursor.close();
return phoneToContactInfoMap;
}
The question
As I wrote, the above code only usually works.
How come it only usually works? What's missing to fix it?
Should I use Contacts.getLookupUri instead of lookup key? If so, how should I change the code above to use it instead?
I tried to use a URI instead of a lookup-key, but then it didn't find any of them inside the normal contacts.
The main issue I see that can explain why users won't see results from your code, is that you're assuming all the contacts are stored on a Google account.
While this is the default behavior in some devices, it's not the default on all devices, also, users can freely change their contacts storage to any other location (yahoo contacts, MS exchange, phone-only (unsynced), etc.)
Having that said, if your only requirement is to
fetch all contacts that have a specified account.
I think that's a much better alternative then your 2 queries (one of which runs over all contacts, not just the required ones):
// Uri to query contacts that have a RawContact in the desired account
final Uri.Builder builder = Contacts.CONTENT_URI.buildUpon();
builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, whatsappAccountName);
builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, whatsappAccountType);
Uri uri = builder.build();
String[] projection = new String[]{ Contacts.LOOKUP_KEY, Contacts._ID Contacts.DISPLAY_NAME }; // add more if needed
// boo-yaa!
Cursor cur = cr.query(uri, projection, null, null, null);

How to Create a Playlist

I am trying to create an app which simply offers an edittext and imagebutton. If the butten gets clicked, the idea is that an album is added to the Playlist, named in the edittext box. Albums should be selected randomly. Goes without saying that the album tracks should be in the correct order.
I can add more functionality later eg. save, overwrite, delete etc.
I have the interface but am struggling with the code. I sort of get the concept of ContentProviders.
so the code needs to:
access the Playlists, which I believe is achieved by using MediaStore.Audio.Playlists
access the Albums, which I believe is achieved by using MediaStore.Audio.Albums
add to the Playlist
I have the following code (most bits obtained from this site. Thanks btw) to access the Playlist but it crashes with a Null Exception error.
public void checkforplaylists()
{
//Get a cursor over all playlists.
final ContentResolver resolver= MediaProvider.mContentResolver;
final Uri uri=MediaStore.Audio.Playlists.INTERNAL_CONTENT_URI;
final String id=MediaStore.Audio.Playlists._ID;
final String name=MediaStore.Audio.Playlists.NAME;
final String[]columns={id,name};
final Cursor playlists= resolver.query(uri, columns, null, null, null);
if(playlists==null)
{
Log.e(TAG,"Found no playlists.");
return;
}
return;
}
Anyone who can help?
I think you mean NullPointerException, which means one of your assignments is null and then you try to access a member of the object you intended it to be. Most likely it is resolver, but to be sure you need the line number reported and/or to step through that with a debugger.
This works. When using the ContentResolver, the Context (this) is required.
public void checkforplaylists(Context context)
{
ContentResolver cr = context.getContentResolver();
final Uri uri=MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
final String id=MediaStore.Audio.Playlists._ID;
final String name=MediaStore.Audio.Playlists.NAME;
final String[]columns={id,name};
final Cursor playlists= cr.query(uri, columns, null, null, null);
if(playlists==null)
{
Log.e(TAG,"Found no playlists.");
return;
}
Log.e(TAG,"Found playlists.");
return;
}
use this code, will work
public boolean addPlaylist(String pname) {
Uri playlists = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
Cursor c = resolver.query(playlists, new String[] { "*" }, null, null,
null);
long playlistId = 0;
c.moveToFirst();
do {
String plname = c.getString(c
.getColumnIndex(MediaStore.Audio.Playlists.NAME));
if (plname.equalsIgnoreCase(pname)) {
playlistId = c.getLong(c
.getColumnIndex(MediaStore.Audio.Playlists._ID));
break;
}
} while (c.moveToNext());
c.close();
if (playlistId != 0) {
Uri deleteUri = ContentUris.withAppendedId(playlists, playlistId);
Log.d(TAG, "REMOVING Existing Playlist: " + playlistId);
// delete the playlist
resolver.delete(deleteUri, null, null);
}
Log.d(TAG, "CREATING PLAYLIST: " + pname);
ContentValues v1 = new ContentValues();
v1.put(MediaStore.Audio.Playlists.NAME, pname);
v1.put(MediaStore.Audio.Playlists.DATE_MODIFIED,
System.currentTimeMillis());
Uri newpl = resolver.insert(playlists, v1);
Log.d(TAG, "Added PlayLIst: " + newpl);
flag=true;
return flag;
}

Retrieving a contacts notes

I'm still very new to android app development, and I have hit a problem I hope u can help me with...
I am trying to retrieve any "Notes" stored against a contact within my phone. I want to check if a contact (current caller) has any notes associated to them, and then either display the contents or do some action depending on the content of them etc...
I have tried the code below as a test to see if the data is retrieved anywhere within my cursor, but although it retrieves some data, I can't see the content of a note - so I guess I'm in the wrong place!
Where I have declared contentID, this is a result of doing a lookup with the code below, and using the id that is recieved.
Uri lookupUri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(inCommingNumber));
String[] mPhoneNumberProjection = { Phone._ID, Phone.NUMBER, Phone.DISPLAY_NAME};
Cursor cur = getContentResolver().query(lookupUri , mPhoneNumberProjection, null, null, null);
private boolean hasNote(String contactID){
Boolean noteFound = false;
Cursor noteCur = getContentResolver().query(ContactsContract.Data.CONTENT_URI, null, null, null, null);
if(noteCur.moveToFirst()) {
int numCols = noteCur.getColumnCount();
if(numCols > 0){
for(int x = 0; x < numCols; x++){
Log.d(APP_TAG, " column " + x + " contains: " + noteCur.getString(x));
}
}
noteFound = true;
} else{
Log.d(APP_TAG, "No Note retrieved");
}
noteCur.close();
return noteFound;
}
Apologies, I cant seem to get the code to display properly in this post!!
Any help would be great
I have tried various things, but can't seem to be able to get this data. I have created the note by simply adding it through the normal contact manager. I'm using Android 2.2.
The following code would display the first note for all contacts on your phone:
Cursor contactsCursor = null;
try {
contactsCursor = getContentResolver().query(RawContacts.CONTENT_URI,
new String [] { RawContacts._ID },
null, null, null);
if (contactsCursor != null && contactsCursor.moveToFirst()) {
do {
String rawContactId = contactsCursor.getString(0);
Cursor noteCursor = null;
try {
noteCursor = getContentResolver().query(Data.CONTENT_URI,
new String[] {Data._ID, Note.NOTE},
Data.RAW_CONTACT_ID + "=?" + " AND "
+ Data.MIMETYPE + "='" + Note.CONTENT_ITEM_TYPE + "'",
new String[] {rawContactId}, null);
if (noteCursor != null && noteCursor.moveToFirst()) {
String note = noteCursor.getString(noteCursor.getColumnIndex(Note.NOTE));
Log.d(APP_TAG, "Note: " + note);
}
} finally {
if (noteCursor != null) {
noteCursor.close();
}
}
} while (contactsCursor.moveToNext());
}
} finally {
if (contactsCursor != null) {
contactsCursor.close();
}
}
If you are storing some sort of structured data on the note to take actions on using the free form note field may not be the best approach. With the new contacts contract model you are able to attach your own data fields (in ContactsContract.Data) to a contact just give them your own unique mimetype.

Cannot find Facebook contacts in RawContacts

I'm trying to build a contacts-managing application. On my phone I have contacts from a couple of accounts including Facebook and HTC Facebook. For some reason I cannot retrieve these contacts from the RawContacts table of ContactsContract:
managedQuery(ContactsContract.RawContacts.CONTENT_URI, new String[] {
ContactsContract.RawContacts._ID,
ContactsContract.RawContacts.CONTACT_ID,
ContactsContract.RawContacts.ACCOUNT_NAME,
ContactsContract.RawContacts.ACCOUNT_TYPE,
}, ContactsContract.RawContacts.ACCOUNT_TYPE + " = 'com.facebook.auth.login'", null, null)
This query returns no results. If I repace the account type with com.htc.socialnetwork.facebook, I still get no results. There are many Facebook contacts on my phone; how to retrieve them?
I think I found the answer here: How to get a contact's facebook id or url from native contacts / content resolver?
Seems that access to Facebook contacts is restricted.
Yes, the Facebook contacts are secured, but can be hijacked with a little sneaky SQLite Injection. NO- I am not going to post here on this page, but is there any reason you do not just use Facebook authentication and get the contacts THAT way? The Facebook contacts don't really have anything useful in them anyway, and it is just more secure and more likely to work ALL the
There is definitely no way to do this in a standard way. So, we must use an SQLi injection (as roger commented) in order to be able to hack the contacts database and get the Facebook avatars. The following code works on most Motorolas, which use Motoblur, on Android 2.2 or higher:
public static Bitmap loadFacebookAvatar(Context context, long personId) {
String[] rawProjection = {ContactsContract.RawContacts._ID};
String contactIdAssertion = ContactsContract.RawContacts.CONTACT_ID + " = " + personId;
String rawWhere = new StringBuilder()
.append(contactIdAssertion).append(") UNION ALL SELECT ")
.append(ContactsContract.RawContacts._ID).append(" FROM view_raw_contacts WHERE (")
.append(contactIdAssertion).toString();
Cursor query = context.getContentResolver().query(ContactsContract.RawContacts.CONTENT_URI,
rawProjection,
rawWhere, null, null);
if (query != null && query.moveToFirst()) {
do {
long id = query.getLong(query.getColumnIndex(ContactsContract.RawContacts._ID));
String[] projection = {ContactsContract.CommonDataKinds.Photo.PHOTO};
Uri uri = ContactsContract.Data.CONTENT_URI;
String mimeTypeAssertion = ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE + "'";
String photoAssertion = ContactsContract.CommonDataKinds.Photo.PHOTO + " IS NOT NULL";
String rawContactIdAssertion = ContactsContract.CommonDataKinds.Photo.RAW_CONTACT_ID + " = " + id;
String where = new StringBuilder().append(mimeTypeAssertion).append(" AND ")
.append(photoAssertion).append(" AND ").append(rawContactIdAssertion)
.append(") UNION ALL SELECT ").append(ContactsContract.CommonDataKinds.Photo.PHOTO)
.append(" FROM view_data WHERE (").append(photoAssertion).append(" AND ")
.append(rawContactIdAssertion).toString();
Cursor photoQuery = context.getContentResolver().query(uri, projection, where, null, null);
if (photoQuery != null && photoQuery.moveToFirst()) {
do {
byte[] photoData = photoQuery.getBlob(photoQuery.getColumnIndex(ContactsContract.CommonDataKinds.Photo.PHOTO));
if (photoData != null) {
return BitmapFactory.decodeByteArray(photoData, 0, photoData.length, null);
}
} while (photoQuery.moveToNext());
}
} while (query.moveToNext());
}
return null;
}
For other handsets you must get the contacts database and analyze it in order to determine how to apply the SQL Injection, which requires a rooted phone.

Categories

Resources