Skip to content

Commit be03a46

Browse files
committed
feat(ContactPicker): Pick single contact on Android
1 parent 5deebaf commit be03a46

File tree

3 files changed

+310
-3
lines changed

3 files changed

+310
-3
lines changed

src/SamplesApp/SamplesApp.Droid/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@
3535
[assembly: UsesPermission("android.permission.ACTIVITY_RECOGNITION")]
3636
[assembly: UsesPermission("android.permission.ACCESS_NETWORK_STATE")]
3737
[assembly: UsesPermission("android.permission.SET_WALLPAPER")]
38+
[assembly: UsesPermission("android.permission.READ_CONTACTS")]
Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
#nullable enable
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Threading.Tasks;
6+
using Android.App;
7+
using Android.Content;
8+
using Android.Database;
9+
using Android.Provider;
10+
using Uno.Extensions;
11+
using Uno.Helpers.Activities;
12+
using Uno.UI;
13+
using Windows.Extensions;
14+
15+
namespace Windows.ApplicationModel.Contacts
16+
{
17+
public partial class ContactPicker
18+
{
19+
private static Task<bool> IsSupportedTaskAsync() => Task.FromResult(true);
20+
21+
private async Task<Contact?> PickContactTaskAsync()
22+
{
23+
if (!await PermissionsHelper.CheckReadContactsPermission(default))
24+
{
25+
if (!await PermissionsHelper.TryGetReadContactsPermission(default))
26+
{
27+
throw new InvalidOperationException("android.permission.READ_CONTACTS permission is required");
28+
}
29+
}
30+
31+
if (ContextHelper.Current == null)
32+
{
33+
throw new InvalidOperationException("Context is not initialized yet, API called too early in application lifecycle.");
34+
}
35+
36+
using var intent = new Intent(Intent.ActionPick);
37+
intent.SetType(ContactsContract.CommonDataKinds.Phone.ContentType);
38+
var activity = await AwaitableResultActivity.StartAsync();
39+
var result = await activity.StartActivityForResultAsync(intent);
40+
41+
if (result?.Intent?.Data is Android.Net.Uri contactUri)
42+
{
43+
using var contentResolver = Application.Context.ContentResolver;
44+
if (contentResolver != null)
45+
{
46+
var projection = new string[] { ContactsContract.ContactsColumns.LookupKey };
47+
using ICursor? cursorLookUpKey = contentResolver.Query(contactUri, projection, null, null, null);
48+
if (cursorLookUpKey?.MoveToFirst() == true)
49+
{
50+
var lookupKey = cursorLookUpKey.GetString(cursorLookUpKey.GetColumnIndex(projection[0]));
51+
if (lookupKey != null)
52+
{
53+
return LookupToContact(lookupKey, contentResolver);
54+
}
55+
}
56+
}
57+
}
58+
return null;
59+
}
60+
61+
private static Contact LookupToContact(string lookupKey, ContentResolver contentResolver)
62+
{
63+
var contact = new Contact();
64+
65+
66+
ReadStructuredName(contact, lookupKey, contentResolver);
67+
68+
//var displayName = cursor.GetString(cursor.GetColumnIndex(ContactsContract.Contacts.InterfaceConsts.DisplayName));
69+
//if (contact.DisplayName != displayName)
70+
//{
71+
// contact.DisplayNameOverride = displayName ?? string.Empty;
72+
//}
73+
74+
//contact.Notes = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.Note.ContentItemType)) ?? string.Empty;
75+
76+
77+
78+
79+
80+
//contact.Phones.AddRange(GetPhones(idQuery, contentResolver));
81+
82+
//contact.Emails.AddRange(GetEmails(idQuery, contentResolver));
83+
84+
//contact.Addresses.AddRange(GetAddresses(idQuery, contentResolver));
85+
86+
return contact;
87+
}
88+
89+
private static void ReadStructuredName(Contact contact, string lookupKey, ContentResolver contentResolver)
90+
{
91+
var contactWhere = ContactsContract.ContactsColumns.LookupKey + " = ?";
92+
var contactWhereParams = new[] { lookupKey };
93+
if (ContactsContract.Data.ContentUri != null)
94+
{
95+
using var cursor = contentResolver.Query(
96+
ContactsContract.Data.ContentUri,
97+
null,
98+
contactWhere,
99+
contactWhereParams,
100+
null
101+
);
102+
if (cursor?.MoveToFirst() == true)
103+
{
104+
contact.HonorificNamePrefix = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.StructuredName.Prefix)) ?? string.Empty;
105+
contact.FirstName = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GivenName)) ?? string.Empty;
106+
contact.MiddleName = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.StructuredName.MiddleName)) ?? string.Empty;
107+
contact.LastName = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FamilyName)) ?? string.Empty;
108+
contact.HonorificNameSuffix = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.StructuredName.Suffix)) ?? string.Empty;
109+
110+
contact.YomiGivenName = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.StructuredName.PhoneticGivenName)) ?? string.Empty;
111+
contact.YomiFamilyName = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.StructuredName.PhoneticFamilyName)) ?? string.Empty;
112+
}
113+
}
114+
}
115+
116+
private static IEnumerable<ContactPhone> GetPhones(string[] idQuery, ContentResolver contentResolver)
117+
{
118+
var contactPhones = new List<ContactPhone>();
119+
var uri = ContactsContract.CommonDataKinds.Phone.ContentUri?.BuildUpon()?.AppendQueryParameter(ContactsContract.RemoveDuplicateEntries, "1")?.Build();
120+
121+
if (uri != null)
122+
{
123+
using ICursor? cursor = contentResolver.Query(
124+
uri,
125+
null,
126+
ContactsContract.Contacts.InterfaceConsts.Id + "=?",
127+
idQuery,
128+
null);
129+
130+
if (cursor != null)
131+
{
132+
if (cursor.MoveToFirst())
133+
{
134+
do
135+
{
136+
var number = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.Phone.Number));
137+
138+
var phoneType = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.Phone.InterfaceConsts.Type));
139+
var kind = TypeToContactPhoneKind(phoneType);
140+
141+
contactPhones.Add(new ContactPhone()
142+
{
143+
Kind = kind,
144+
Number = number ?? string.Empty
145+
});
146+
}
147+
while (cursor.MoveToNext());
148+
}
149+
cursor.Close();
150+
}
151+
}
152+
return contactPhones;
153+
}
154+
155+
private static IEnumerable<ContactEmail> GetEmails(string[] idQuery, ContentResolver contentResolver)
156+
{
157+
var contactEmails = new List<ContactEmail>();
158+
var uri = ContactsContract.CommonDataKinds.Email.ContentUri?.BuildUpon()?.AppendQueryParameter(ContactsContract.RemoveDuplicateEntries, "1")?.Build();
159+
160+
if (uri != null)
161+
{
162+
using ICursor? cursor = contentResolver.Query(
163+
uri,
164+
null,
165+
ContactsContract.Contacts.InterfaceConsts.Id + "=?",
166+
idQuery,
167+
null);
168+
169+
if (cursor != null)
170+
{
171+
if (cursor.MoveToFirst())
172+
{
173+
do
174+
{
175+
var address = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.Email.Address));
176+
177+
var emailType = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.Email.InterfaceConsts.Type));
178+
var kind = TypeToContactEmailKind(emailType);
179+
180+
contactEmails.Add(new ContactEmail()
181+
{
182+
Kind = kind,
183+
Address = address ?? string.Empty
184+
});
185+
}
186+
while (cursor.MoveToNext());
187+
}
188+
cursor.Close();
189+
}
190+
}
191+
return contactEmails;
192+
}
193+
194+
private static IEnumerable<ContactAddress> GetAddresses(string[] idQuery, ContentResolver contentResolver)
195+
{
196+
var contactAddresss = new List<ContactAddress>();
197+
var uri = ContactsContract.CommonDataKinds.StructuredPostal.ContentUri?.BuildUpon()?.AppendQueryParameter(ContactsContract.RemoveDuplicateEntries, "1")?.Build();
198+
199+
if (uri != null)
200+
{
201+
using ICursor? cursor = contentResolver.Query(
202+
uri,
203+
null,
204+
ContactsContract.Contacts.InterfaceConsts.Id + "=?",
205+
idQuery,
206+
null);
207+
208+
if (cursor != null)
209+
{
210+
if (cursor.MoveToFirst())
211+
{
212+
do
213+
{
214+
var city = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.City));
215+
var country = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.Country));
216+
var postalCode = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.Postcode));
217+
var region = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.Region));
218+
var street = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.Street));
219+
220+
var addressType = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.InterfaceConsts.Type));
221+
var kind = TypeToContactAddressKind(addressType);
222+
223+
contactAddresss.Add(new ContactAddress()
224+
{
225+
Kind = kind,
226+
Country = country ?? string.Empty,
227+
Locality = city ?? string.Empty,
228+
PostalCode = postalCode ?? string.Empty,
229+
Region = region ?? string.Empty,
230+
StreetAddress = street ?? string.Empty
231+
});
232+
}
233+
while (cursor.MoveToNext());
234+
}
235+
cursor.Close();
236+
}
237+
}
238+
return contactAddresss;
239+
}
240+
241+
private static ContactPhoneKind TypeToContactPhoneKind(string? type)
242+
{
243+
if (int.TryParse(type, out var typeInt))
244+
{
245+
if (Enum.IsDefined(typeof(PhoneDataKind), typeInt))
246+
{
247+
var PhoneType = (PhoneDataKind)typeInt;
248+
return PhoneType switch
249+
{
250+
PhoneDataKind.Assistant => ContactPhoneKind.Assistant,
251+
PhoneDataKind.CompanyMain => ContactPhoneKind.Company,
252+
PhoneDataKind.Main => ContactPhoneKind.Mobile,
253+
PhoneDataKind.Mobile => ContactPhoneKind.Mobile,
254+
PhoneDataKind.Home => ContactPhoneKind.Home,
255+
PhoneDataKind.FaxHome => ContactPhoneKind.HomeFax,
256+
PhoneDataKind.Pager => ContactPhoneKind.Pager,
257+
PhoneDataKind.Radio => ContactPhoneKind.Radio,
258+
PhoneDataKind.Work => ContactPhoneKind.Work,
259+
PhoneDataKind.WorkMobile => ContactPhoneKind.Work,
260+
PhoneDataKind.WorkPager => ContactPhoneKind.Work,
261+
PhoneDataKind.FaxWork => ContactPhoneKind.BusinessFax,
262+
_ => ContactPhoneKind.Other
263+
};
264+
}
265+
}
266+
return ContactPhoneKind.Other;
267+
}
268+
269+
private static ContactEmailKind TypeToContactEmailKind(string? type)
270+
{
271+
if (int.TryParse(type, out var typeInt))
272+
{
273+
if (Enum.IsDefined(typeof(EmailDataKind), typeInt))
274+
{
275+
var EmailType = (EmailDataKind)typeInt;
276+
return EmailType switch
277+
{
278+
EmailDataKind.Home => ContactEmailKind.Personal,
279+
EmailDataKind.Mobile => ContactEmailKind.Personal,
280+
EmailDataKind.Work => ContactEmailKind.Work,
281+
_ => ContactEmailKind.Other
282+
};
283+
}
284+
}
285+
return ContactEmailKind.Other;
286+
}
287+
288+
private static ContactAddressKind TypeToContactAddressKind(string? type)
289+
{
290+
if (int.TryParse(type, out var typeInt))
291+
{
292+
if (Enum.IsDefined(typeof(AddressDataKind), typeInt))
293+
{
294+
var addressType = (AddressDataKind)typeInt;
295+
return addressType switch
296+
{
297+
AddressDataKind.Home => ContactAddressKind.Home,
298+
AddressDataKind.Work => ContactAddressKind.Work,
299+
_ => ContactAddressKind.Other
300+
};
301+
}
302+
}
303+
return ContactAddressKind.Other;
304+
}
305+
}
306+
}

src/Uno.UWP/Generated/3.0.0.0/Windows.ApplicationModel.Contacts/ContactPicker.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#pragma warning disable 114 // new keyword hiding
33
namespace Windows.ApplicationModel.Contacts
44
{
5-
#if __ANDROID__ || false || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
5+
#if false
66
[global::Uno.NotImplemented]
77
#endif
88
public partial class ContactPicker
@@ -65,7 +65,7 @@ public string CommitButtonText
6565
}
6666
}
6767
#endif
68-
#if __ANDROID__ || false || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
68+
#if false
6969
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
7070
public ContactPicker()
7171
{
@@ -93,7 +93,7 @@ public ContactPicker()
9393
}
9494
#endif
9595
// Forced skipping of method Windows.ApplicationModel.Contacts.ContactPicker.DesiredFieldsWithContactFieldType.get
96-
#if __ANDROID__ || false || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
96+
#if false
9797
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
9898
public global::Windows.Foundation.IAsyncOperation<global::Windows.ApplicationModel.Contacts.Contact> PickContactAsync()
9999
{

0 commit comments

Comments
 (0)