0

I'm trying to query contact phone numbers and names. As this method is written, I pretty sure it's going to give me a new name/number entry for each phone number. I'll worry about listing numbers under a single contact after I get it working as-is...

I'm getting a cursor index out of bounds error on line 160 but not sure why (still pretty fresh with contacts). I've pasted the method popping the error as well as the logcat.

Please help me figure out this error. Also, if you know of a more efficient way to do the query, I'd greatly appreciate it.

Thanks in advance

Edit:

Thanks to Graham Borland, I see that my simple mistake was the position of "moveToNext()". Moving it to the end of the for loop fixed the crash. I was right that it's creating a new entry for each phone number. But I have this contact with 3 numbers so it gave 3 entries, which for now I'm OK with, but it gave the same number in each entry. Any ideas?

private void getContacts() {

    String name, number, label;

    uri = CommonDataKinds.Phone.CONTENT_URI;
    projection = new String[] { CommonDataKinds.Phone.DISPLAY_NAME,
            CommonDataKinds.Phone.NUMBER, CommonDataKinds.Phone.LABEL };
    selection = null;
    selectionArgs = null;
    sortOrder = CommonDataKinds.Phone.DISPLAY_NAME;

    Cursor peopleCursor = getContentResolver().query(uri, projection,
            selection, selectionArgs, sortOrder);

    if (peopleCursor != null) {

        int indexName = peopleCursor
                .getColumnIndex(CommonDataKinds.Phone.DISPLAY_NAME);
        int indexNumber = peopleCursor
                .getColumnIndex(CommonDataKinds.Phone.NUMBER);
        int indexLabel = peopleCursor
                .getColumnIndex(CommonDataKinds.Phone.LABEL);

        peopleCursor.moveToFirst();

        for (int i = 0; i < peopleCursor.getCount(); i++) {

            ContactNameItems nameItems = new ContactNameItems();
            ContactPhoneItems phoneItems = new ContactPhoneItems();
            numberList = new ArrayList<ContactPhoneItems>();

            peopleCursor.moveToNext();

            name = peopleCursor.getString(indexName);  // This is line 160
            number = peopleCursor.getString(indexNumber);
            label = peopleCursor.getString(indexLabel);

            if (name != null && !name.isEmpty()) {

                nameItems.setName(name);
                mListDataHeader.add(nameItems);

                if (number != null && !number.isEmpty()) {

                    phoneItems.setNumber(number);
                    phoneItems.setPhoneType(label);

                    numberList.add(phoneItems);

                    mListDataChild.put(mListDataHeader.get(i).getName(),
                            numberList);
                    mListAdapter.notifyDataSetChanged();

                }
            }
        }
    }
    peopleCursor.close();
}



01-22 21:42:21.347: E/AndroidRuntime(793): FATAL EXCEPTION: main
01-22 21:42:21.347: E/AndroidRuntime(793): java.lang.RuntimeException: Unable to resume activity {com.psesto.expandablelistview/com.psesto.expandablelistview.MainActivity}: android.database.CursorIndexOutOfBoundsException: Index 5 requested, with a size of 5
01-22 21:42:21.347: E/AndroidRuntime(793):  at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2742)
01-22 21:42:21.347: E/AndroidRuntime(793):  at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2771)
01-22 21:42:21.347: E/AndroidRuntime(793):  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2235)
01-22 21:42:21.347: E/AndroidRuntime(793):  at android.app.ActivityThread.access$600(ActivityThread.java:141)
01-22 21:42:21.347: E/AndroidRuntime(793):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
01-22 21:42:21.347: E/AndroidRuntime(793):  at android.os.Handler.dispatchMessage(Handler.java:99)
01-22 21:42:21.347: E/AndroidRuntime(793):  at android.os.Looper.loop(Looper.java:137)
01-22 21:42:21.347: E/AndroidRuntime(793):  at android.app.ActivityThread.main(ActivityThread.java:5041)
01-22 21:42:21.347: E/AndroidRuntime(793):  at java.lang.reflect.Method.invokeNative(Native Method)
01-22 21:42:21.347: E/AndroidRuntime(793):  at java.lang.reflect.Method.invoke(Method.java:511)
01-22 21:42:21.347: E/AndroidRuntime(793):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
01-22 21:42:21.347: E/AndroidRuntime(793):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
01-22 21:42:21.347: E/AndroidRuntime(793):  at dalvik.system.NativeStart.main(Native Method)
01-22 21:42:21.347: E/AndroidRuntime(793): Caused by: android.database.CursorIndexOutOfBoundsException: Index 5 requested, with a size of 5
01-22 21:42:21.347: E/AndroidRuntime(793):  at android.database.AbstractCursor.checkPosition(AbstractCursor.java:424)
01-22 21:42:21.347: E/AndroidRuntime(793):  at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:136)
01-22 21:42:21.347: E/AndroidRuntime(793):  at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:50)
01-22 21:42:21.347: E/AndroidRuntime(793):  at android.database.CursorWrapper.getString(CursorWrapper.java:114)
01-22 21:42:21.347: E/AndroidRuntime(793):  at com.psesto.expandablelistview.MainActivity.getContacts(MainActivity.java:160)
01-22 21:42:21.347: E/AndroidRuntime(793):  at com.psesto.expandablelistview.MainActivity.onResume(MainActivity.java:58)
01-22 21:42:21.347: E/AndroidRuntime(793):  at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1185)
01-22 21:42:21.347: E/AndroidRuntime(793):  at android.app.Activity.performResume(Activity.java:5182)
01-22 21:42:21.347: E/AndroidRuntime(793):  at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2732)
01-22 21:42:21.347: E/AndroidRuntime(793):  ... 12 more

The fix:

private void getContacts() {

String name, number, label;

uri = CommonDataKinds.Phone.CONTENT_URI;
projection = new String[] { CommonDataKinds.Phone.DISPLAY_NAME,
        CommonDataKinds.Phone.NUMBER, CommonDataKinds.Phone.LABEL };
selection = null;
selectionArgs = null;
sortOrder = CommonDataKinds.Phone.DISPLAY_NAME;

Cursor peopleCursor = getContentResolver().query(uri, projection,
        selection, selectionArgs, sortOrder);

if (peopleCursor != null) {

    int indexName = peopleCursor
            .getColumnIndex(CommonDataKinds.Phone.DISPLAY_NAME);
    int indexNumber = peopleCursor
            .getColumnIndex(CommonDataKinds.Phone.NUMBER);
    int indexLabel = peopleCursor
            .getColumnIndex(CommonDataKinds.Phone.LABEL);

    peopleCursor.moveToFirst();

    for (int i = 0; i < peopleCursor.getCount(); i++) {

        ContactNameItems nameItems = new ContactNameItems();
        ContactPhoneItems phoneItems = new ContactPhoneItems();
        numberList = new ArrayList<ContactPhoneItems>();

        name = peopleCursor.getString(indexName);  // This is line 160
        number = peopleCursor.getString(indexNumber);
        label = peopleCursor.getString(indexLabel);

        if (name != null && !name.isEmpty()) {

            nameItems.setName(name);
            mListDataHeader.add(nameItems);

            if (number != null && !number.isEmpty()) {

                phoneItems.setNumber(number);
                phoneItems.setPhoneType(label);

                numberList.add(phoneItems);

                mListDataChild.put(mListDataHeader.get(i).getName(),
                        numberList);
                mListAdapter.notifyDataSetChanged();

            }
        }

        peopleCursor.moveToNext();
    }
}
peopleCursor.close();

}

Psest328
  • 6,575
  • 11
  • 55
  • 90

2 Answers2

2

Try this for clean iteration:

for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
    // do what you need with the cursor here
}

This is a typical off by one mistake. Cursor.moveToFirst() already moves the cursor to first position, and then Cursor.moveToNext() moves it to the next one skipping the first.

To make your code work delete the Cursor.moveToFirst() from your code.

On a side note, if you look for cleaner way of dealing with cursors and content providers try this library: https://github.com/futuresimple/android-db-commons

It would change the vital part of your code to something like this:

   numberList = ProviderAction.query(CommonDataKinds.Phone.CONTENT_URI)
        .projection(CommonDataKinds.Phone.DISPLAY_NAME,
            CommonDataKinds.Phone.NUMBER,
            CommonDataKinds.Phone.LABEL)
        .perform(getContentResolver())
        .toFluentIterable(new Function<Cursor, ContactPhoneItems>() {
          @Override
          public ContactPhoneItems apply(Cursor cursor) {
            ContactPhoneItems item = new ContactPhoneItems();
            item.setNumber(cursor.getString(cursor.getColumnIndexOrThrow(CommonDataKinds.Phone.NUMBER)));
            item.setPhoneType(cursor.getString(cursor.getColumnIndexOrThrow(CommonDataKinds.Phone.LABEL)));
            return item;
          }

        })
        .filter(new Predicate<ContactPhoneItems>() {
          @Override
          public boolean apply(ContactPhoneItems contactPhoneItems) {
            return !TextUtils.isEmpty(contactPhoneItems.getNumber());
          }
        });
Bartek Filipowicz
  • 1,235
  • 7
  • 9
0
 peopleCursor.moveToFirst();

 for (int i = 0; i < peopleCursor.getCount(); i++) {

     // do some setup
     peopleCursor.moveToNext();
     // read data from the cursor

This is a very complicated, and wrong, way of iterating through the cursor. When you read data for the first time, you've already moved onto the second row (moveToFirst() followed by moveToNext()). And by the 5th iteration of the loop, you've gone past the end of the results, which is causing the error.

Use a simple while loop instead.

while (peopleCursor.moveToNext()) {
    ....
}

See What's the best way to iterate an Android Cursor? for more explanation.

Community
  • 1
  • 1
Graham Borland
  • 60,055
  • 21
  • 138
  • 179
  • I need to add the value of an arraylist, which is why I'm using the for (if you know how to do the same with a 'while', I'm all ears). But I see what you're saying about the position of 'moveToNext'. I'm going to move it to the end of the loop and see what happens – Psest328 Jan 23 '14 at 00:38
  • Ok... moving it to the end fixed the crash (Yay!). And I was right that it's creating a new entry for each phone number. But I have this contact with 3 numbers so it gave 3 entries, which for now I'm OK with, but it gave the same number in each entry. Any ideas? – Psest328 Jan 23 '14 at 00:43
  • @WizardKnight That's a different story all together. There's a nice training covering Android Contacts access: http://developer.android.com/training/contacts-provider/index.html – Bartek Filipowicz Jan 23 '14 at 01:20