Search in sources :

Example 1 with PhoneLookupInfo

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;
    }));
}
Also used : ContentProviderOperation(android.content.ContentProviderOperation) Assert(com.android.dialer.common.Assert) Context(android.content.Context) LogUtil(com.android.dialer.common.LogUtil) MoreExecutors(com.google.common.util.concurrent.MoreExecutors) Arrays(java.util.Arrays) ListenableFuture(com.google.common.util.concurrent.ListenableFuture) AnnotatedCallLog(com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog) RemoteException(android.os.RemoteException) Callable(java.util.concurrent.Callable) CallLogDataSource(com.android.dialer.calllog.datasources.CallLogDataSource) CallLogMutations(com.android.dialer.calllog.datasources.CallLogMutations) ArrayList(java.util.ArrayList) Inject(javax.inject.Inject) BackgroundExecutor(com.android.dialer.common.concurrent.Annotations.BackgroundExecutor) PhoneLookup(com.android.dialer.phonelookup.PhoneLookup) CompositePhoneLookup(com.android.dialer.phonelookup.composite.CompositePhoneLookup) Map(java.util.Map) ArraySet(android.util.ArraySet) PhoneLookupHistoryDatabaseHelper(com.android.dialer.phonelookup.database.PhoneLookupHistoryDatabaseHelper) PhoneLookupHistory(com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory) MainThread(android.support.annotation.MainThread) ApplicationContext(com.android.dialer.inject.ApplicationContext) Cursor(android.database.Cursor) ArrayMap(android.util.ArrayMap) InvalidProtocolBufferException(com.google.protobuf.InvalidProtocolBufferException) LightweightExecutor(com.android.dialer.common.concurrent.Annotations.LightweightExecutor) ImmutableSet(com.google.common.collect.ImmutableSet) PhoneLookupInfo(com.android.dialer.phonelookup.PhoneLookupInfo) PhoneLookupHistoryContract(com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract) ImmutableMap(com.google.common.collect.ImmutableMap) Set(java.util.Set) TextUtils(android.text.TextUtils) WorkerThread(android.support.annotation.WorkerThread) Maps(com.google.common.collect.Maps) DialerPhoneNumber(com.android.dialer.DialerPhoneNumber) OperationApplicationException(android.content.OperationApplicationException) Futures(com.google.common.util.concurrent.Futures) Entry(java.util.Map.Entry) NumberAttributesBuilder(com.android.dialer.calllogutils.NumberAttributesBuilder) ContentValues(android.content.ContentValues) ListeningExecutorService(com.google.common.util.concurrent.ListeningExecutorService) PhoneLookupInfo(com.android.dialer.phonelookup.PhoneLookupInfo) DialerPhoneNumber(com.android.dialer.DialerPhoneNumber) InvalidProtocolBufferException(com.google.protobuf.InvalidProtocolBufferException) ArrayMap(android.util.ArrayMap) Cursor(android.database.Cursor)

Example 2 with 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);
    }
}
Also used : ContentValues(android.content.ContentValues) PhoneLookupInfo(com.android.dialer.phonelookup.PhoneLookupInfo)

Example 3 with PhoneLookupInfo

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);
        }
    }
}
Also used : ContentValues(android.content.ContentValues) PhoneLookupInfo(com.android.dialer.phonelookup.PhoneLookupInfo)

Example 4 with 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;
}
Also used : ContentValues(android.content.ContentValues) ContentProviderOperation(android.content.ContentProviderOperation) PhoneLookupInfo(com.android.dialer.phonelookup.PhoneLookupInfo) ArrayList(java.util.ArrayList) WorkerThread(android.support.annotation.WorkerThread)

Example 5 with PhoneLookupInfo

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);
}
Also used : ContentValues(android.content.ContentValues) ContentProviderOperation(android.content.ContentProviderOperation) PhoneLookupInfo(com.android.dialer.phonelookup.PhoneLookupInfo) ArrayList(java.util.ArrayList) DialerPhoneNumber(com.android.dialer.DialerPhoneNumber) MainThread(android.support.annotation.MainThread)

Aggregations

PhoneLookupInfo (com.android.dialer.phonelookup.PhoneLookupInfo)12 ContentValues (android.content.ContentValues)6 DialerPhoneNumber (com.android.dialer.DialerPhoneNumber)5 ArrayList (java.util.ArrayList)5 Cursor (android.database.Cursor)4 InvalidProtocolBufferException (com.google.protobuf.InvalidProtocolBufferException)4 ContentProviderOperation (android.content.ContentProviderOperation)3 MainThread (android.support.annotation.MainThread)3 PhoneLookup (com.android.dialer.phonelookup.PhoneLookup)3 ImmutableMap (com.google.common.collect.ImmutableMap)3 ListenableFuture (com.google.common.util.concurrent.ListenableFuture)3 Context (android.content.Context)2 WorkerThread (android.support.annotation.WorkerThread)2 TextUtils (android.text.TextUtils)2 Assert (com.android.dialer.common.Assert)2 LogUtil (com.android.dialer.common.LogUtil)2 BackgroundExecutor (com.android.dialer.common.concurrent.Annotations.BackgroundExecutor)2 LightweightExecutor (com.android.dialer.common.concurrent.Annotations.LightweightExecutor)2 ApplicationContext (com.android.dialer.inject.ApplicationContext)2 PhoneLookupHistory (com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory)2