21

I have an array of phone numbers and I want to get the corresponding contact names from the contacts database.

In the array of phone numbers, I also have some numbers that are not saved before to the contact database. For example;

  • 3333333 -> Tim
  • 5555555 -> Jim
  • 1111111 -> unknown

I have the array containing the phone numbers shown above, namely phoneArr.

int size=phoneArr.size();
if(size>0){
        Cursor[] cursors=new Cursor[size];
        for(int i=0;i<size;i++){
            Uri contactUri1 = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneArr.get(i)));
            cursors[i] = getContentResolver().query(contactUri1, PEOPLE_PROJECTION, null, null, " _id asc limit 1");
        }
        Cursor phones=new MergeCursor(cursors);

phones.getCount() returns 2 in the above scenario. When the phone number does not appear in the contact list the cursor becomes empty and somehow when I merge them it doesn't contribute anything at all. What I want is to have a cursor as follows

Cursor phones -> {Tim, Jim, 1111111}

I think I can do this by adding the row manually as follows:

Uri contactUri1 = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneArr.get(i)));
cursors[i] = getContentResolver().query(contactUri1, PEOPLE_PROJECTION, null, null, " _id asc limit 1");
if(cursors[i].getCount()==0)
    // add the phone number manually to the cursor

How can I achieve this?

Here is the PEOPLE_PROJECTION

private static final String[] PEOPLE_PROJECTION = new String[] {
    ContactsContract.PhoneLookup._ID,
    ContactsContract.PhoneLookup.DISPLAY_NAME,
    ContactsContract.PhoneLookup.NUMBER
};
2
  • Please review the answer you accepted, it seem to be wrong according to the currently available features of the Android SDK. Commented Mar 14, 2014 at 11:04
  • Well, by the time I accepted the answer, it seemed reasonable. I will review it when I have some spare time. Commented Mar 14, 2014 at 13:22

4 Answers 4

57

Easiest way to add rows in a cursor is to use a MatrixCursor and a MergeCursor. Those two classes are from the SDK and here to solve that kind of problems.

Basically what you do is :

  1. Put the rows you want to add in a MatrixCusror
  2. Merge your cursor and your matrixCursor using a MergeCursor

Something like:

// Create a MatrixCursor filled with the rows you want to add.
MatrixCursor matrixCursor = new MatrixCursor(new String[] { colName1, colName2 });
matrixCursor.addRow(new Object[] { value1, value2 });

// Merge your existing cursor with the matrixCursor you created.
MergeCursor mergeCursor = new MergeCursor(new Cursor[] { matrixCursor, cursor });

// Use your the mergeCursor as you would use your cursor.
Sign up to request clarification or add additional context in comments.

5 Comments

Would you put this at the top of the bindView method?
I think it's better to do this before passing the cursor to the adapter. Right after you made the query.
add what would be the index of this new row in the mergeCursor ?
@Nezam, from the above example, the new row will be the first in position
@Nazam, You need to create an index column named _id in the MatrixCursor. The values in that index column will be set by addRow(). They can be whatever you like, as the MatrixCursor is not a SQL database, and it doesn't matter if they overlap with the _id values in the other Cursor.
3

I use a solution that does the trick for all my different needs, and which is simpler than implementing Cursor.
Here is an example where I have to add extra "playlist" rows to a Cursor, retrieved from the Mediastore. I add rows at first indexes of the original Cursor :

public class CursorWithExtraPlaylists extends CursorWrapper {

public static final int ID_COLUMN_INDEX = 0;
public static final int NAME_COLUMN_INDEX = 1;

private int mPos = -1;
private Context mContext;
private Playlist[] extras;

public CursorWithExtraPlaylists(Cursor cursor, Context context,
        Playlist[] extras) {
    super(cursor);
    mContext = context;
    this.extras = extras;
}

@Override
public int getCount() {
    return super.getCount() + extras.length;
}

@Override
public int getPosition() {
    return mPos;
}

@Override
public boolean moveToFirst() {
    return moveToPosition(0);
}

@Override
public boolean moveToLast() {
    return moveToPosition(getCount() - extras.length);
}

@Override
public boolean move(int offset) {
    return moveToPosition(mPos + offset);
}

@Override
public boolean moveToPosition(int position) {
    // Make sure position isn't past the end of the cursor
    final int count = getCount();
    if (position >= count) {
        mPos = count;
        return false;
    }
    // Make sure position isn't before the beginning of the cursor
    if (position < 0) {
        mPos = -1;
        return false;
    }
    // Check for no-op moves, and skip the rest of the work for them
    if (position == mPos) {
        return true;
    }

    mPos = position;
    if (mPos > 0) {
        // ok, that concerns super cursor
        return super.moveToPosition(mPos - 1);
    }
    return true;
}

@Override
public boolean moveToNext() {
    return moveToPosition(mPos + 1);
}

@Override
public boolean moveToPrevious() {
    return moveToPosition(mPos - 1);
}

private boolean isPointingOnExtras() {
    if (-1 == mPos || getCount() == mPos) {
        throw new CursorIndexOutOfBoundsException(mPos, getCount());
    }
    if (mPos <= extras.length - 1) {
        // this is a request on an extra value
        return true;
    }
    return false;
}

private Object getExtraValue(int columnIndex) {
    switch (columnIndex) {
    case ID_COLUMN_INDEX:
        return extras[mPos].getId();
    case NAME_COLUMN_INDEX:
        return extras[mPos].getName(mContext);
    }
    return null;
}

@Override
public int getInt(int columnIndex) {
    if (isPointingOnExtras())
        return (Integer) getExtraValue(columnIndex);
    int res = super.getInt(columnIndex);
    return res;
}

@Override
public String getString(int columnIndex) {

    if (isPointingOnExtras())
        return getExtraValue(columnIndex).toString();
    return super.getString(columnIndex);
}

public static class Playlist {
    private int mId;
    private int mNameResId;

    public Playlist(int mId, int nameResId) {
        this.mId = mId;
        this.mNameResId = nameResId;
    }

    public int getId() {
        return mId;
    }

    public String getName(Context mContext) {
        return mContext.getResources().getString(mNameResId);
    }

}
}

Original cursor has 2 columns (int,String), so I construct it with an array of extra rows objects.

Comments

2

I know this an old post, but it is a real pain that rows can not be added manually to a Cursor. I have come up with a crude solution. It might be of some help.

Cursor is actually an interface and you can create a custom class that implements the Cursor interface.

class ManualCursor implements Cursor{
      // ....
      private Vector<String[]> rows;

      public void addRow(String[] values){
            rows.add(values);
      }

      // Implement the necessary methods, getString(int), etc.

      //....
}

And finally in your code

if (cursors[i].getCount() == 0){
     cursors[i] = new ManualCursor();
     cursors[i].addRow(rowValues);
}

That should do it. But be warned though, for this to work, you have to be wise while implementing the Cursor interface.

1 Comment

That works, you could use a MatrixCursor for that as well, but you then have a potential memory problem, and you remove the large data list efficiency of RecyclerView.
1

Unfortunately, As far as I know, there is no way that you can manually add data to the cursor. You need to handle this in a different way.

The only way that I can think of in which you can do this is

  • Make a structure with the data fields you need.
  • Make a Arraylist and fill the objects of your structure with the data from the cursors and add them to the list.
  • Now add your missing number information manually and add them to the list.
  • Pass this list to your adapter, in case u r passing ure cursor to an adapter now. Of course you would have to change your cursoradapter implementation to an arrayadapter.

2 Comments

Well this is not what I want to hear:) Anyway thanks for answering
Ya i know. Its a major pain. I have cursed a lot cause I faced this numerous times. However if you do manage to find a way to add to cursor (which I really doubt), do share on this post. And as a fallback you could use this method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.