1package eu.siacs.conversations.android;
2
3import android.Manifest;
4import android.content.Context;
5import android.content.pm.PackageManager;
6import android.database.Cursor;
7import android.net.Uri;
8import android.os.Build;
9import android.provider.ContactsContract;
10import android.util.Log;
11
12import com.google.common.collect.ImmutableMap;
13import com.google.common.collect.HashMultimap;
14import com.google.common.collect.Multimap;
15
16import java.util.ArrayList;
17import java.util.Collection;
18import java.util.Collections;
19import java.util.HashMap;
20import java.util.Map;
21
22import eu.siacs.conversations.Config;
23import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
24import io.michaelrocks.libphonenumber.android.NumberParseException;
25
26public class PhoneNumberContact extends AbstractPhoneContact {
27
28 private final String phoneNumber;
29 private final String typeLabel;
30 private final Collection<String> groups;
31
32 public String getPhoneNumber() {
33 return phoneNumber;
34 }
35
36 public String getTypeLabel() {
37 return typeLabel;
38 }
39
40 public Collection<String> getTags() {
41 Collection<String> tags = new ArrayList(groups);
42 tags.add(typeLabel);
43 return tags;
44 }
45
46 public Collection<String> getGroups() {
47 return groups;
48 }
49
50 private PhoneNumberContact(Context context, Cursor cursor, Collection<String> groups) throws IllegalArgumentException {
51 super(cursor);
52 try {
53 this.phoneNumber = PhoneNumberUtilWrapper.normalize(context, cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
54 this.typeLabel = ContactsContract.CommonDataKinds.Phone.getTypeLabel(
55 context.getResources(),
56 cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE)),
57 cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.LABEL))
58 ).toString();
59 this.groups = groups;
60 } catch (NumberParseException | NullPointerException e) {
61 throw new IllegalArgumentException(e);
62 }
63 }
64
65 public static ImmutableMap<String, PhoneNumberContact> load(Context context) {
66 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
67 return ImmutableMap.of();
68 }
69
70 final HashMap<String, String> groupTitles = new HashMap<>();
71 try (final Cursor groups = context.getContentResolver().query(
72 ContactsContract.Groups.CONTENT_URI,
73 new String[]{
74 ContactsContract.Groups._ID,
75 ContactsContract.Groups.TITLE
76 }, null, null, null
77 )) {
78 while (groups != null && groups.moveToNext()) {
79 groupTitles.put(groups.getString(0), groups.getString(1));
80 }
81 } catch (final Exception e) {
82 return ImmutableMap.of();
83 }
84
85 final Multimap<String, String> contactGroupMap = HashMultimap.create();
86 try(final Cursor contactGroups = context.getContentResolver().query(
87 ContactsContract.Data.CONTENT_URI,
88 new String[]{
89 ContactsContract.Data.CONTACT_ID,
90 ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID
91 },
92 ContactsContract.Data.MIMETYPE + "=?",
93 new String[]{ContactsContract.CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE},
94 null
95 )) {
96 while (contactGroups != null && contactGroups.moveToNext()) {
97 final String groupTitle = groupTitles.get(contactGroups.getString(1));
98 if (groupTitle != null) contactGroupMap.put(contactGroups.getString(0), groupTitle);
99 }
100 } catch (final Exception e) {
101 return ImmutableMap.of();
102 }
103
104 final String[] PROJECTION = new String[]{
105 ContactsContract.Data._ID,
106 ContactsContract.Data.CONTACT_ID,
107 ContactsContract.Data.DISPLAY_NAME,
108 ContactsContract.Data.PHOTO_URI,
109 ContactsContract.Data.LOOKUP_KEY,
110 ContactsContract.CommonDataKinds.Phone.TYPE,
111 ContactsContract.CommonDataKinds.Phone.LABEL,
112 ContactsContract.CommonDataKinds.Phone.NUMBER};
113 final HashMap<String, PhoneNumberContact> contacts = new HashMap<>();
114 try (final Cursor cursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PROJECTION, null, null, null)){
115 while (cursor != null && cursor.moveToNext()) {
116 try {
117 final PhoneNumberContact contact = new PhoneNumberContact(context, cursor, contactGroupMap.get(cursor.getString(1)));
118 final PhoneNumberContact preexisting = contacts.get(contact.getPhoneNumber());
119 if (preexisting == null || preexisting.rating() < contact.rating()) {
120 contacts.put(contact.getPhoneNumber(), contact);
121 }
122 } catch (final IllegalArgumentException ignored) {
123
124 }
125 }
126 } catch (final Exception e) {
127 return ImmutableMap.of();
128 }
129 return ImmutableMap.copyOf(contacts);
130 }
131
132 public static PhoneNumberContact findByUriOrNumber(Collection<PhoneNumberContact> haystack, Uri uri, String number) {
133 final PhoneNumberContact byUri = findByUri(haystack, uri);
134 return byUri != null || number == null ? byUri : findByNumber(haystack, number);
135 }
136
137 public static PhoneNumberContact findByUri(Collection<PhoneNumberContact> haystack, Uri needle) {
138 for (PhoneNumberContact contact : haystack) {
139 if (needle.equals(contact.getLookupUri())) {
140 return contact;
141 }
142 }
143 return null;
144 }
145
146 private static PhoneNumberContact findByNumber(Collection<PhoneNumberContact> haystack, String needle) {
147 for (PhoneNumberContact contact : haystack) {
148 if (needle.equals(contact.getPhoneNumber())) {
149 return contact;
150 }
151 }
152 return null;
153 }
154}