use of com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info.Cp2ContactInfo in project android_packages_apps_Dialer by LineageOS.
the class Cp2DefaultDirectoryPhoneLookup method batchQueryForValidNumbers.
private ListenableFuture<Map<String, Set<Cp2ContactInfo>>> batchQueryForValidNumbers(Set<String> validE164Numbers) {
return backgroundExecutorService.submit(() -> {
Map<String, Set<Cp2ContactInfo>> cp2ContactInfosByNumber = new ArrayMap<>();
if (validE164Numbers.isEmpty()) {
return cp2ContactInfosByNumber;
}
try (Cursor cursor = queryPhoneTableBasedOnE164(Cp2Projections.getProjectionForPhoneTable(), validE164Numbers)) {
if (cursor == null) {
LogUtil.w("Cp2DefaultDirectoryPhoneLookup.batchQueryForValidNumbers", "null cursor");
} else {
while (cursor.moveToNext()) {
String validE164Number = Cp2Projections.getNormalizedNumberFromCursor(cursor);
Set<Cp2ContactInfo> cp2ContactInfos = cp2ContactInfosByNumber.get(validE164Number);
if (cp2ContactInfos == null) {
cp2ContactInfos = new ArraySet<>();
cp2ContactInfosByNumber.put(validE164Number, cp2ContactInfos);
}
cp2ContactInfos.add(Cp2Projections.buildCp2ContactInfoFromCursor(appContext, cursor, Directory.DEFAULT));
}
}
}
return cp2ContactInfosByNumber;
});
}
use of com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info.Cp2ContactInfo in project android_packages_apps_Dialer by LineageOS.
the class Cp2DefaultDirectoryPhoneLookup method lookupInternal.
private Cp2Info lookupInternal(DialerPhoneNumber dialerPhoneNumber) {
String number = dialerPhoneNumber.getNormalizedNumber();
if (TextUtils.isEmpty(number)) {
return Cp2Info.getDefaultInstance();
}
Set<Cp2ContactInfo> cp2ContactInfos = new ArraySet<>();
// Even though this is only a single number, use PartitionedNumbers to mimic the logic used
// during getMostRecentInfo.
PartitionedNumbers partitionedNumbers = new PartitionedNumbers(ImmutableSet.of(dialerPhoneNumber));
Cursor cursor = null;
try {
// to ensure consistency when the batch methods are used to update data.
if (!partitionedNumbers.validE164Numbers().isEmpty()) {
cursor = queryPhoneTableBasedOnE164(Cp2Projections.getProjectionForPhoneTable(), partitionedNumbers.validE164Numbers());
} else {
cursor = queryPhoneLookup(Cp2Projections.getProjectionForPhoneLookupTable(), Iterables.getOnlyElement(partitionedNumbers.invalidNumbers()));
}
if (cursor == null) {
LogUtil.w("Cp2DefaultDirectoryPhoneLookup.lookupInternal", "null cursor");
return Cp2Info.getDefaultInstance();
}
while (cursor.moveToNext()) {
cp2ContactInfos.add(Cp2Projections.buildCp2ContactInfoFromCursor(appContext, cursor, Directory.DEFAULT));
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return Cp2Info.newBuilder().addAllCp2ContactInfo(cp2ContactInfos).build();
}
use of com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info.Cp2ContactInfo in project android_packages_apps_Dialer by LineageOS.
the class Cp2DefaultDirectoryPhoneLookup method buildMapForUpdatedOrAddedContacts.
/**
* 1. get all contact ids. if the id is unset, add the number to the list of contacts to look up.
* 2. reduce our list of contact ids to those that were updated after lastModified. 3. Now we have
* the smallest set of dialer phone numbers to query cp2 against. 4. build and return the map of
* dialerphonenumbers to their new Cp2ContactInfo
*
* @return Map of {@link DialerPhoneNumber} to {@link Cp2Info} with updated {@link
* Cp2ContactInfo}.
*/
private ListenableFuture<Map<DialerPhoneNumber, Set<Cp2ContactInfo>>> buildMapForUpdatedOrAddedContacts(Map<DialerPhoneNumber, Cp2Info> existingInfoMap, long lastModified, Set<DialerPhoneNumber> deletedPhoneNumbers) {
// Start by building a set of DialerPhoneNumbers that we want to update.
ListenableFuture<Set<DialerPhoneNumber>> updatedNumbersFuture = findNumbersToUpdate(existingInfoMap, lastModified, deletedPhoneNumbers);
return Futures.transformAsync(updatedNumbersFuture, updatedNumbers -> {
if (updatedNumbers.isEmpty()) {
return Futures.immediateFuture(new ArrayMap<>());
}
// Divide the numbers into those that are valid and those that are not. Issue a single
// batch query for the valid numbers against the PHONE table, and in parallel issue
// individual queries against PHONE_LOOKUP for each invalid number.
// TODO(zachh): These queries are inefficient without a lastModified column to filter on.
PartitionedNumbers partitionedNumbers = new PartitionedNumbers(ImmutableSet.copyOf(updatedNumbers));
ListenableFuture<Map<String, Set<Cp2ContactInfo>>> validNumbersFuture = batchQueryForValidNumbers(partitionedNumbers.validE164Numbers());
List<ListenableFuture<Set<Cp2ContactInfo>>> invalidNumbersFuturesList = new ArrayList<>();
for (String invalidNumber : partitionedNumbers.invalidNumbers()) {
invalidNumbersFuturesList.add(individualQueryForInvalidNumber(invalidNumber));
}
ListenableFuture<List<Set<Cp2ContactInfo>>> invalidNumbersFuture = Futures.allAsList(invalidNumbersFuturesList);
Callable<Map<DialerPhoneNumber, Set<Cp2ContactInfo>>> computeMap = () -> {
// These get() calls are safe because we are using whenAllSucceed below.
Map<String, Set<Cp2ContactInfo>> validNumbersResult = validNumbersFuture.get();
List<Set<Cp2ContactInfo>> invalidNumbersResult = invalidNumbersFuture.get();
Map<DialerPhoneNumber, Set<Cp2ContactInfo>> map = new ArrayMap<>();
// First update the map with the valid number results.
for (Entry<String, Set<Cp2ContactInfo>> entry : validNumbersResult.entrySet()) {
String validNumber = entry.getKey();
Set<Cp2ContactInfo> cp2ContactInfos = entry.getValue();
Set<DialerPhoneNumber> dialerPhoneNumbers = partitionedNumbers.dialerPhoneNumbersForValidE164(validNumber);
addInfo(map, dialerPhoneNumbers, cp2ContactInfos);
// We are going to remove the numbers that we've handled so that we later can
// detect numbers that weren't handled and therefore need to have their contact
// information removed.
updatedNumbers.removeAll(dialerPhoneNumbers);
}
// Next update the map with the invalid results.
int i = 0;
for (String invalidNumber : partitionedNumbers.invalidNumbers()) {
Set<Cp2ContactInfo> cp2Infos = invalidNumbersResult.get(i++);
Set<DialerPhoneNumber> dialerPhoneNumbers = partitionedNumbers.dialerPhoneNumbersForInvalid(invalidNumber);
addInfo(map, dialerPhoneNumbers, cp2Infos);
// We are going to remove the numbers that we've handled so that we later can
// detect numbers that weren't handled and therefore need to have their contact
// information removed.
updatedNumbers.removeAll(dialerPhoneNumbers);
}
// information for them.
for (DialerPhoneNumber dialerPhoneNumber : updatedNumbers) {
map.put(dialerPhoneNumber, ImmutableSet.of());
}
LogUtil.v("Cp2DefaultDirectoryPhoneLookup.buildMapForUpdatedOrAddedContacts", "found %d numbers that may need updating", updatedNumbers.size());
return map;
};
return Futures.whenAllSucceed(validNumbersFuture, invalidNumbersFuture).call(computeMap, lightweightExecutorService);
}, lightweightExecutorService);
}
use of com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info.Cp2ContactInfo in project android_packages_apps_Dialer by LineageOS.
the class Cp2DefaultDirectoryPhoneLookup method findDialerPhoneNumbersContainingContactId.
private static Set<DialerPhoneNumber> findDialerPhoneNumbersContainingContactId(Map<DialerPhoneNumber, Cp2Info> existingInfoMap, long contactId) {
Set<DialerPhoneNumber> matches = new ArraySet<>();
for (Entry<DialerPhoneNumber, Cp2Info> entry : existingInfoMap.entrySet()) {
for (Cp2ContactInfo cp2ContactInfo : entry.getValue().getCp2ContactInfoList()) {
if (cp2ContactInfo.getContactId() == contactId) {
matches.add(entry.getKey());
}
}
}
Assert.checkArgument(matches.size() > 0, "Couldn't find DialerPhoneNumber for contact ID: " + contactId);
return matches;
}
use of com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info.Cp2ContactInfo in project android_packages_apps_Dialer by LineageOS.
the class Cp2DefaultDirectoryPhoneLookup method getMostRecentInfo.
@Override
public ListenableFuture<ImmutableMap<DialerPhoneNumber, Cp2Info>> getMostRecentInfo(ImmutableMap<DialerPhoneNumber, Cp2Info> existingInfoMap) {
currentLastTimestampProcessed = null;
if (!PermissionsUtil.hasContactsReadPermissions(appContext)) {
LogUtil.w("Cp2DefaultDirectoryPhoneLookup.getMostRecentInfo", "missing permissions");
return missingPermissionsOperations.getMostRecentInfoForMissingPermissions(existingInfoMap);
}
ListenableFuture<Long> lastModifiedFuture = backgroundExecutorService.submit(() -> sharedPreferences.getLong(PREF_LAST_TIMESTAMP_PROCESSED, 0L));
return Futures.transformAsync(lastModifiedFuture, lastModified -> {
// Build a set of each DialerPhoneNumber that was associated with a contact, and is no
// longer associated with that same contact.
ListenableFuture<Set<DialerPhoneNumber>> deletedPhoneNumbersFuture = getDeletedPhoneNumbers(existingInfoMap, lastModified);
return Futures.transformAsync(deletedPhoneNumbersFuture, deletedPhoneNumbers -> {
// If there are too many invalid numbers, just defer the work to render time.
ArraySet<DialerPhoneNumber> unprocessableNumbers = findUnprocessableNumbers(existingInfoMap);
Map<DialerPhoneNumber, Cp2Info> existingInfoMapToProcess = existingInfoMap;
if (!unprocessableNumbers.isEmpty()) {
existingInfoMapToProcess = Maps.filterKeys(existingInfoMap, number -> !unprocessableNumbers.contains(number));
}
// For each DialerPhoneNumber that was associated with a contact or added to a
// contact, build a map of those DialerPhoneNumbers to a set Cp2ContactInfos, where
// each Cp2ContactInfo represents a contact.
ListenableFuture<Map<DialerPhoneNumber, Set<Cp2ContactInfo>>> updatedContactsFuture = buildMapForUpdatedOrAddedContacts(existingInfoMapToProcess, lastModified, deletedPhoneNumbers);
return Futures.transform(updatedContactsFuture, updatedContacts -> {
// Start build a new map of updated info. This will replace existing info.
ImmutableMap.Builder<DialerPhoneNumber, Cp2Info> newInfoMapBuilder = ImmutableMap.builder();
// For each DialerPhoneNumber in existing info...
for (Entry<DialerPhoneNumber, Cp2Info> entry : existingInfoMap.entrySet()) {
DialerPhoneNumber dialerPhoneNumber = entry.getKey();
Cp2Info existingInfo = entry.getValue();
// Build off the existing info
Cp2Info.Builder infoBuilder = Cp2Info.newBuilder(existingInfo);
// If the contact was updated, replace the Cp2ContactInfo list
if (updatedContacts.containsKey(dialerPhoneNumber)) {
infoBuilder.clear().addAllCp2ContactInfo(updatedContacts.get(dialerPhoneNumber));
// If it was deleted and not added to a new contact, clear all the CP2
// information.
} else if (deletedPhoneNumbers.contains(dialerPhoneNumber)) {
infoBuilder.clear();
} else if (unprocessableNumbers.contains(dialerPhoneNumber)) {
// does not have the ability to fetch information at render time).
if (!dialerPhoneNumber.getNormalizedNumber().isEmpty()) {
// Don't clear the existing info when the number is unprocessable. It's
// likely that the existing info is up-to-date so keep it in place so
// that the UI doesn't pop when the query is completed at display time.
infoBuilder.setIsIncomplete(true);
}
}
// If the DialerPhoneNumber didn't change, add the unchanged existing info.
newInfoMapBuilder.put(dialerPhoneNumber, infoBuilder.build());
}
return newInfoMapBuilder.build();
}, lightweightExecutorService);
}, lightweightExecutorService);
}, lightweightExecutorService);
}
Aggregations