Feature request: Get contacts related to an account
ReveredMachine opened this issue · 9 comments
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}
)
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.
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)
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.
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 ?
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 CNContainer
s. 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 togetContacts
- Add a
returnUnifiedContacts
(defaulting to true) option togetContacts
- Map
CNContainer
s toAccounts
on iOS
Added includeNonVisibleOnAndroid
and returnUnifiedContacts
to version 0.2.2. @ReveredMachine let me know if that works for you!
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 ?
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.
@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.