Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Get contacts related to an account #5

Open
ReveredMachine opened this issue Feb 21, 2021 · 9 comments
Open

Feature request: Get contacts related to an account #5

ReveredMachine opened this issue Feb 21, 2021 · 9 comments

Comments

@ReveredMachine
Copy link

It would be nice to have a possibility to get all contacts that are assigned to an account.

Like this:

/// All contacts for the provided account will be fetched.
/// In case of no provided account, all contacts will be fetched
static Future<List<Contact>> getContacts(
          {bool withPhotos = false,
          bool sorted = true,
          Account account}
)
@joachimvalente
Copy link
Contributor

Hi! Thank you for the suggestion. What would be the use case, concretely? One thing to note is that Account is an Android-only thing.

@ReveredMachine
Copy link
Author

The use case is, to get a list of contacts having

  • all contacts that I have created for my account (regardless if they are visible or not)
  • all visible contacts

With this I can get all the contacts I own and all the visible ones.

My idea was, If an account is

  • not provided: All visible contacts will be returned (Current behavior)
  • provided:
    ->Android: All visible contacts and that ones for the provided account (This feature request)
    ->Ios: All visible contacts will be returned (Current behavior)

@joachimvalente
Copy link
Contributor

joachimvalente commented Feb 22, 2021

Ah, that makes sense. The plugin doesn't yet support querying/searching, but you can always filter after the fact. For example:

final contacts = (await FlutterContacts
    .getContacts())
    .where((c) => c.accounts.any((a) => a.type == 'com.myapp'))
    .toList();

That said there's no option to include non-visible contacts on Android. We could just add one so that you could write:

final contacts = (await FlutterContacts
    .getContacts(includeNonVisibleOnAndroid: true))
    .where((c) => c.accounts.any((a) => a.type == 'com.myapp'))
    .toList();

Would that work for you?

I guess one issue with that approach is that you'd get the properties from all raw accounts, not just your account.

@ReveredMachine
Copy link
Author

Hi,

yes that would work for me and yes, with that approach it is much easier to read all contacts (not only the own ones) with flutter. Maybe you can put a hint to the documentation to use the includeNonVisibleOnAndroid with care.

For the IOS side I found the CNContainer class, do you know if there is a relation to the Android-Accounts ?

@joachimvalente
Copy link
Contributor

Hmm, possibly! Thanks for flagging. I'll give it a try and if it indeed is the equivalent of raw contacts, I'll update the API so that Accounts represent those CNContainers. I might also add an option to return raw contacts separately, instead of unified contacts, in which case Accounts would always be of size 1, making it easier to filter just what you created in your app, as opposed to returning all the properties for any unified contact that happens to have a raw contact from your app.

In summary:

  • Add an includeNonVisibleOnAndroid (defaulting to false) option to getContacts
  • Add a returnUnifiedContacts (defaulting to true) option to getContacts
  • Map CNContainers to Accounts on iOS

@joachimvalente
Copy link
Contributor

Added includeNonVisibleOnAndroid and returnUnifiedContacts to version 0.2.2. @ReveredMachine let me know if that works for you!

@ReveredMachine
Copy link
Author

@joachimvalente

With includeNonVisibleOnAndroid=true all contacts will be returned, that is working.

But unfortunately I cannot filter for a specific account as they are not filled in the result because the awesome getQuick method does not provide them. A question is how to check on the flutter side if the returned contact was originally a visible one in case the contact's account does not belongs to me ?

After testing I was somtime wondering, maybe I found an issue

After saving a contact, it will be returned as follow,

select(
      resolver,
      rawId.toString(), /*with_properties=*/ true, /*with_thumbnail=*/true,
      /*withPhoto=*/true, /*returnUnifiedContacts=*/true,
      /*includeNonVisible=*/true, /*idIsRawContactId=*/true

If I am not wrong, this leads to a selection by the rawId (idIsRawContactId), but due to returnUnifiedContacts=true, the id in the returned contact is filled with the UnifiedContactsId

val id = if (returnUnifiedContacts) getString(Data.CONTACT_ID) else getString(Data.RAW_CONTACT_ID)

One thing regarding contacts and unified contacts

if (!contact.isUnified) {
  throw Exception('Cannot insert raw contacts');
}

From my knowledge it's the unified contact (android: ContactsContract.Contacts) which cannot be added in android

Maybe I can provide some code from my previous proof of concept ?

@joachimvalente
Copy link
Contributor

But unfortunately I cannot filter for a specific account as they are not filled in the result because the awesome getQuick method does not provide them.

You can't use getQuick if you need accounts. You'll have to fetch properties too:

contacts = (await FlutterContact.getContacts(withProperties: true, includeNonVisibleOnAndroid: true))
    .where((c) => c.accounts.any((a) => a.type == 'com.myapp'))
    .toList();

A question is how to check on the flutter side if the returned contact was originally a visible one in case the contact's account does not belongs to me ?

Currently, you can't easily do that. You could fetch once with includeNonVisibleOnAndroid: true and once with includeNonVisibleOnAndroid: false. That's hacky and not very efficient, but I'm reluctant to add too much Android-specific stuff in the API, so I'd rather not expose whether a contact is "visible." Of course, you can always fork this repo and make local changes. (I'm doing that myself to fit my app's specific needs.)

After testing I was somtime wondering, maybe I found an issue

After saving a contact, it will be returned as follow,

select(
      resolver,
      rawId.toString(), /*with_properties=*/ true, /*with_thumbnail=*/true,
      /*withPhoto=*/true, /*returnUnifiedContacts=*/true,
      /*includeNonVisible=*/true, /*idIsRawContactId=*/true

If I am not wrong, this leads to a selection by the rawId (idIsRawContactId), but due to returnUnifiedContacts=true, the id in the returned contact is filled with the UnifiedContactsId

val id = if (returnUnifiedContacts) getString(Data.CONTACT_ID) else getString(Data.RAW_CONTACT_ID)

Correct. But why is that an issue? The /*returnUnifiedContacts=*/true is because I didn't implement inserting raw contacts yet, so returnUnifiedContacts has to be true at that point. The /*idIsRawContactId=*/true means that we just inserted a contact, and in this case Android actually gives us the raw ID. That's one of the reasons we need to select() again here, so that we can return the contact with unified ID.

From my knowledge it's the unified contact (android: ContactsContract.Contacts) which cannot be added in android

You're right! But I didn't want to simply mimic how Android works, or how iOS work, since they're different. Instead I tried to make an API that's most compatible with both platforms.

Ultimately we do want to be able to update/insert/delete raw contacts too, it's just that I haven't figured out all the details yet. For example, if you change the name in a raw contact, what are you going to do with the name in the other raw contacts? With unified contacts, it's much simpler since you can update the entire contact at once.

Maybe I can provide some code from my previous proof of concept ?

Please do! Any help is welcome :) And feel free to open a pull request if that's easier.

@ReveredMachine
Copy link
Author

@joachimvalente
Ahh, ok, with /returnUnifiedContacts=/true I meant only that the save method always returns the unified contact regardless of the setting FlutterContactsConfig.returnUnifiedContacts. Correct, it's not an issue, I just have to keep that in mind.

You can take a look to the attached patch file that already has the deletion and last modification timestamp (See my feature request #8), but these changes are for now android specific and may not work with IOS :-(

And yes, you are right an API should be as much as possible decoupled from the native side.

flutter_contacts_issue_5_and_8_patch.txt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants