use of com.android.dialer.phonelookup.PhoneLookupInfo in project android_packages_apps_Dialer by LineageOS.
the class PhoneLookupDataSource method queryPhoneLookupHistoryForNumbers.
/**
* Returned map must have same keys as {@code uniqueDialerPhoneNumbers}
*/
private ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> queryPhoneLookupHistoryForNumbers(Context appContext, Set<DialerPhoneNumber> uniqueDialerPhoneNumbers) {
// Note: This loses country info when number is not valid.
Map<DialerPhoneNumber, String> dialerPhoneNumberToNormalizedNumbers = Maps.asMap(uniqueDialerPhoneNumbers, DialerPhoneNumber::getNormalizedNumber);
// Convert values to a set to remove any duplicates that are the result of two
// DialerPhoneNumbers mapping to the same normalized number.
String[] normalizedNumbers = dialerPhoneNumberToNormalizedNumbers.values().toArray(new String[] {});
String[] questionMarks = new String[normalizedNumbers.length];
Arrays.fill(questionMarks, "?");
String selection = PhoneLookupHistory.NORMALIZED_NUMBER + " in (" + TextUtils.join(",", questionMarks) + ")";
Map<String, PhoneLookupInfo> normalizedNumberToInfoMap = new ArrayMap<>();
try (Cursor cursor = appContext.getContentResolver().query(PhoneLookupHistory.CONTENT_URI, new String[] { PhoneLookupHistory.NORMALIZED_NUMBER, PhoneLookupHistory.PHONE_LOOKUP_INFO }, selection, normalizedNumbers, null)) {
if (cursor == null) {
LogUtil.e("PhoneLookupDataSource.queryPhoneLookupHistoryForNumbers", "null cursor");
} else if (cursor.moveToFirst()) {
int normalizedNumberColumn = cursor.getColumnIndexOrThrow(PhoneLookupHistory.NORMALIZED_NUMBER);
int phoneLookupInfoColumn = cursor.getColumnIndexOrThrow(PhoneLookupHistory.PHONE_LOOKUP_INFO);
do {
String normalizedNumber = cursor.getString(normalizedNumberColumn);
PhoneLookupInfo phoneLookupInfo;
try {
phoneLookupInfo = PhoneLookupInfo.parseFrom(cursor.getBlob(phoneLookupInfoColumn));
} catch (InvalidProtocolBufferException e) {
throw new IllegalStateException(e);
}
normalizedNumberToInfoMap.put(normalizedNumber, phoneLookupInfo);
} while (cursor.moveToNext());
}
}
// number instead of DialerPhoneNumber. Build and return a new map keyed by DialerPhoneNumber.
return ImmutableMap.copyOf(Maps.asMap(uniqueDialerPhoneNumbers, (dialerPhoneNumber) -> {
String normalizedNumber = dialerPhoneNumberToNormalizedNumbers.get(dialerPhoneNumber);
PhoneLookupInfo phoneLookupInfo = normalizedNumberToInfoMap.get(normalizedNumber);
// entry for a number. Just use an empty value for that case.
return phoneLookupInfo == null ? PhoneLookupInfo.getDefaultInstance() : phoneLookupInfo;
}));
}
use of com.android.dialer.phonelookup.PhoneLookupInfo in project android_packages_apps_Dialer by LineageOS.
the class PhoneLookupDataSource method updateMutations.
private void updateMutations(ImmutableMap<Long, PhoneLookupInfo> updatesToApply, CallLogMutations mutations) {
for (Entry<Long, PhoneLookupInfo> entry : updatesToApply.entrySet()) {
long id = entry.getKey();
PhoneLookupInfo phoneLookupInfo = entry.getValue();
ContentValues contentValuesToInsert = mutations.getInserts().get(id);
if (contentValuesToInsert != null) {
/*
* This is a confusing case. Consider:
*
* 1) An incoming call from "Bob" arrives; "Bob" is written to PhoneLookupHistory.
* 2) User changes Bob's name to "Robert".
* 3) User opens call log, and this code is invoked with the inserted call as a mutation.
*
* In populateInserts, we retrieved "Bob" from PhoneLookupHistory and wrote it to the insert
* mutation, which is wrong. We need to actually ask the phone lookups for the most up to
* date information ("Robert"), and update the "insert" mutation again.
*
* Having understood this, you may wonder why populateInserts() is needed at all--excellent
* question! Consider:
*
* 1) An incoming call from number 123 ("Bob") arrives at time T1; "Bob" is written to
* PhoneLookupHistory.
* 2) User opens call log at time T2 and "Bob" is written to it, and everything is fine; the
* call log can be considered accurate as of T2.
* 3) An incoming call from number 456 ("John") arrives at time T3. Let's say the contact
* info for John was last modified at time T0.
* 4) Now imagine that populateInserts() didn't exist; the phone lookup will ask for any
* information for phone number 456 which has changed since T2--but "John" hasn't changed
* since then so no contact information would be found.
*
* The populateInserts() method avoids this problem by always first populating inserted
* mutations from PhoneLookupHistory; in this case "John" would be copied during
* populateInserts() and there wouldn't be further updates needed here.
*/
updateContentValues(contentValuesToInsert, phoneLookupInfo);
continue;
}
ContentValues contentValuesToUpdate = mutations.getUpdates().get(id);
if (contentValuesToUpdate != null) {
updateContentValues(contentValuesToUpdate, phoneLookupInfo);
continue;
}
// Else this row is not already scheduled for insert or update and we need to schedule it.
ContentValues contentValues = new ContentValues();
updateContentValues(contentValues, phoneLookupInfo);
mutations.getUpdates().put(id, contentValues);
}
}
use of com.android.dialer.phonelookup.PhoneLookupInfo in project android_packages_apps_Dialer by LineageOS.
the class PhoneLookupDataSource method populateInserts.
private void populateInserts(ImmutableMap<Long, PhoneLookupInfo> existingInfo, CallLogMutations mutations) {
for (Entry<Long, ContentValues> entry : mutations.getInserts().entrySet()) {
long id = entry.getKey();
ContentValues contentValues = entry.getValue();
PhoneLookupInfo phoneLookupInfo = existingInfo.get(id);
// Existing info might be missing if data was cleared or for other reasons.
if (phoneLookupInfo != null) {
updateContentValues(contentValues, phoneLookupInfo);
}
}
}
use of com.android.dialer.phonelookup.PhoneLookupInfo in project android_packages_apps_Dialer by LineageOS.
the class PhoneLookupDataSource method writePhoneLookupHistory.
@WorkerThread
private Void writePhoneLookupHistory(Context appContext) throws RemoteException, OperationApplicationException {
ArrayList<ContentProviderOperation> operations = new ArrayList<>();
long currentTimestamp = System.currentTimeMillis();
for (Entry<String, PhoneLookupInfo> entry : phoneLookupHistoryRowsToUpdate.entrySet()) {
String normalizedNumber = entry.getKey();
PhoneLookupInfo phoneLookupInfo = entry.getValue();
ContentValues contentValues = new ContentValues();
contentValues.put(PhoneLookupHistory.PHONE_LOOKUP_INFO, phoneLookupInfo.toByteArray());
contentValues.put(PhoneLookupHistory.LAST_MODIFIED, currentTimestamp);
operations.add(ContentProviderOperation.newUpdate(PhoneLookupHistory.contentUriForNumber(normalizedNumber)).withValues(contentValues).build());
}
for (String normalizedNumber : phoneLookupHistoryRowsToDelete) {
operations.add(ContentProviderOperation.newDelete(PhoneLookupHistory.contentUriForNumber(normalizedNumber)).build());
}
Assert.isNotNull(appContext.getContentResolver().applyBatch(PhoneLookupHistoryContract.AUTHORITY, operations));
return null;
}
use of com.android.dialer.phonelookup.PhoneLookupInfo in project android_packages_apps_Dialer by LineageOS.
the class RealtimeRowProcessor method writePhoneLookupHistory.
@MainThread
private void writePhoneLookupHistory() {
Assert.isMainThread();
// Copy the batch to a new collection that be safely processed on a background thread.
ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> currentBatch = ImmutableMap.copyOf(queuedPhoneLookupHistoryWrites);
// Clear the queue, handing responsibility for its items to the background task.
queuedPhoneLookupHistoryWrites.clear();
// Returns the number of rows updated.
ListenableFuture<Integer> applyBatchFuture = backgroundExecutor.submit(() -> {
ArrayList<ContentProviderOperation> operations = new ArrayList<>();
long currentTimestamp = System.currentTimeMillis();
for (Entry<DialerPhoneNumber, PhoneLookupInfo> entry : currentBatch.entrySet()) {
DialerPhoneNumber dialerPhoneNumber = entry.getKey();
PhoneLookupInfo phoneLookupInfo = entry.getValue();
// Note: Multiple DialerPhoneNumbers can map to the same normalized number but we
// just write them all and the value for the last one will arbitrarily win.
// Note: This loses country info when number is not valid.
String normalizedNumber = dialerPhoneNumber.getNormalizedNumber();
ContentValues contentValues = new ContentValues();
contentValues.put(PhoneLookupHistory.PHONE_LOOKUP_INFO, phoneLookupInfo.toByteArray());
contentValues.put(PhoneLookupHistory.LAST_MODIFIED, currentTimestamp);
operations.add(ContentProviderOperation.newUpdate(PhoneLookupHistory.contentUriForNumber(normalizedNumber)).withValues(contentValues).build());
}
return Assert.isNotNull(appContext.getContentResolver().applyBatch(PhoneLookupHistoryContract.AUTHORITY, operations)).length;
});
Futures.addCallback(applyBatchFuture, new FutureCallback<Integer>() {
@Override
public void onSuccess(Integer rowsAffected) {
LogUtil.i("RealtimeRowProcessor.onSuccess", "wrote %d rows to PhoneLookupHistory", rowsAffected);
}
@Override
public void onFailure(Throwable throwable) {
throw new RuntimeException(throwable);
}
}, uiExecutor);
}
Aggregations