2

I have this code (thankfully provided by @EpicPandaForce) and I have problem with deletion. When I add new contact it works like a charm while when I delete contact (or number from that contact if there are two of them) it stays persisted in Realm. How can I get it working properly?

realm.executeTransaction(new Realm.Transaction() {
        @Override
        public void execute(Realm realm) {
            Contact realmContact = new Contact();
            String filter = "" + ContactsContract.Contacts.HAS_PHONE_NUMBER + " > 0 and "
                    + ContactsContract.CommonDataKinds.Phone.TYPE +"="
                    + ContactsContract.CommonDataKinds.Phone.TYPE_MAIN;

            Cursor phones = getActivity()
                    .getContentResolver()
                    .query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, filter, null, null);

            while (phones.moveToNext()) {
                String id = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID));
                String name = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                String phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                realmContact.setId(id);
                realmContact.setName(name);
                realmContact.setNumber(phoneNumber);
                realmContact.setIsBeingSaved(true);
                realm.insertOrUpdate(realmContact);
            }

            /** merge mechanism */
            realm.where(Contact.class)
                    .equalTo("isBeingSaved", false)
                    .findAll()
                    .deleteAllFromRealm(); // delete all non-saved data
            for(Contact contact : realm.where(Contact.class).findAll()) {
                realmContact.setIsBeingSaved(false); // reset all save state
            }

Contact.class

public class Contact extends RealmObject{

    @PrimaryKey
    private String id;

    @Index
    private String name;

    @Index
    private String number;

    @Index
    private boolean isBeingSaved;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public boolean getIsBeingSaved() {
        return isBeingSaved;
    }

    public void setIsBeingSaved(boolean beingSaved) {
        isBeingSaved = beingSaved;
    }
}

EDIT - working code:

Contact realmContact = new Contact();

            Uri uri = Contacts.CONTENT_URI;

            String selection = "((" + CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY + " NOTNULL) AND ("
                    + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
                    + CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY + " != '' ))";

            Cursor phones = getActivity()
                    .getContentResolver()
                    .query(uri, null, selection, null, null);

            String phoneNumber = "";
            while (phones.moveToNext()) {
                String id = phones.getString(phones.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
                String name = phones.getString(phones.getColumnIndex(Contacts.DISPLAY_NAME_PRIMARY));
                String lastTimeContacted = phones.getString(phones.getColumnIndex(Contacts.LAST_TIME_CONTACTED));

                if(Integer.parseInt(phones.getString(phones.getColumnIndex(Contacts.HAS_PHONE_NUMBER))) > 0){
                    Cursor pCur = getActivity().getContentResolver().query(
                            CommonDataKinds.Phone.CONTENT_URI,
                            null,
                            CommonDataKinds.Phone.LOOKUP_KEY +" = ?",
                            new String[]{id}, null);

                    while (pCur.moveToNext()) {
                        phoneNumber += "/" + pCur.getString(pCur.getColumnIndex(CommonDataKinds.Phone.NUMBER));
                        realmContact.setNumber(phoneNumber);
                    }
                    phoneNumber = "";
                    pCur.close();
                } else {
                    realmContact.setNumber("1234");
                }
                realmContact.setId(id);
                realmContact.setName(lastTimeContacted);
                realmContact.setIsBeingSaved(true);
                realm.insertOrUpdate(realmContact);
            }

            Log.i("asd-size", realm.where(Contact.class).findAll().size() + "");

            /** merge mechanism */
            realm.where(Contact.class)
                    .equalTo("isBeingSaved", false)
                    .findAll()
                    .deleteAllFromRealm(); // delete all non-saved data


            for(Contact contact : realm.where(Contact.class).findAll()) {
                contact.setIsBeingSaved(false); // reset all save state
            }
jean d'arme
  • 4,033
  • 6
  • 35
  • 70
  • @EpicPandaForce :) – jean d'arme Sep 07 '16 at 13:41
  • Apart from the `deleteAllFromRealm()` call to delete contacts that are no longer retrieved by the Content Provider, what elements are you trying to delete from the Realm[?](http://stackoverflow.com/questions/39352489/how-to-read-contacts-in-android-using-realm) – EpicPandaForce Sep 07 '16 at 13:43
  • Have you tried to perform insertion and deletion in different transactions? – Divers Sep 07 '16 at 13:44
  • @EpicPandaForce The ones that are not in phone's contact list anymore – jean d'arme Sep 07 '16 at 13:45
  • @Divers there should be no need for that, every query in a transaction reads on the latest version of the data available in that transaction. – EpicPandaForce Sep 07 '16 at 13:45
  • @Divers I though I am – jean d'arme Sep 07 '16 at 13:45
  • I guess the real question is, does it actually get removed from the Content Provider when you delete the contact. Maybe it has something to do with this [raw contact deleted flag shenanigan](http://stackoverflow.com/a/35196076/2413303) and the content provider still returns deleted contacts? – EpicPandaForce Sep 07 '16 at 13:46
  • Strangely, when I add new contact Stetho doesn't notice anything. Even when I restard app and Stetho inspect tool still no refresh while in app new contact is visible. – jean d'arme Sep 07 '16 at 14:07
  • Honestly I haven't used `stetho-realm`. I tend to debug and see what's going on like that. I think the issue is that the Contact still exists even when you've deleted it, so it is still returned by the Content Provider query and you don't delete it with the merge as a result. – EpicPandaForce Sep 07 '16 at 14:09
  • I found this http://stackoverflow.com/questions/15812967/understanding-architecture-of-android-contacts - I have went through documentation and let me say there are TONS of these columns. Second thing - `_ID` can change so it's preferred to identify contact by its name rather than ID. – jean d'arme Sep 07 '16 at 22:29
  • 1
    @EpicPandaForce Just found that `DELETED` column is documented, but not implemented :D Source: http://stackoverflow.com/questions/30609515/how-do-implicit-joined-columns-work-with-android-contacts-data Seems that there are workaround, but still normally it's the other way around :) – jean d'arme Sep 08 '16 at 08:54
  • 1
    ...man, contacts are stupidly overcomplicated -_- – EpicPandaForce Sep 08 '16 at 08:57
  • 1
    If nothing happens regarding this one, I'm going to put a bounty on it when it becomes available – EpicPandaForce Sep 09 '16 at 10:36
  • 1
    In the meantime, I found that `LOOKUP_KEY` is recommended over `_ID` as the latter can change when aggregation happens: http://stackoverflow.com/questions/5151885/android-new-data-record-is-added-to-the-wrong-contact/5158059#5158059 – jean d'arme Sep 09 '16 at 17:39
  • 1
    @EpicPandaForce I found the answer and I hope You won't be mad at me for that... – jean d'arme Sep 10 '16 at 10:55

1 Answers1

0

Okay, after quite long searching I found that there was a typo:

        /** merge mechanism */
        realm.where(Contact.class)
                .equalTo("isBeingSaved", false)
                .findAll()
                .deleteAllFromRealm(); // delete all non-saved data
        for(Contact contact : realm.where(Contact.class).findAll()) {
            realmContact.setIsBeingSaved(false); <- here it is
        }

It's realmContact when in fact it should be contact so it refers to contact iterated by for loop.

So, basically, it was setting Contact realmContact = new Contact(); to false. So the proper version is:

           /** merge mechanism */
            realm.where(Contact.class)
                    .equalTo("isBeingSaved", false)
                    .findAll()
                    .deleteAllFromRealm(); // delete all non-saved data
            for(Contact contact : realm.where(Contact.class).findAll()) {
                contact.setIsBeingSaved(false);
            }
jean d'arme
  • 4,033
  • 6
  • 35
  • 70
  • Hahaha oh well! In the meantime, what's your new contact query from the content provider? After all, they say `_ID` isn't very stable – EpicPandaForce Sep 10 '16 at 11:03
  • 1
    I updated question with actual code :) I now use `LOOKUP_KEY` which is much more promising - instead of i.e. `3245` You get something that looks more like a hash code. Also, I have updated `selection`. Still got WhatsApp duplicate numbers, but I though `Maybe someone will use that channel instead of phone?` so I left it as it is, but now all the numbers are under one contact :) – jean d'arme Sep 10 '16 at 11:15