diff options
Diffstat (limited to 'java/com/android/dialer/calllog')
5 files changed, 142 insertions, 31 deletions
diff --git a/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java b/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java index f8c6fcef1..e2e112cd0 100644 --- a/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java +++ b/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java @@ -18,6 +18,7 @@ package com.android.dialer.calllog; import android.content.Context; import android.content.SharedPreferences; +import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor; import com.android.dialer.configprovider.ConfigProviderBindings; import com.android.dialer.inject.ApplicationContext; @@ -39,7 +40,7 @@ public final class AnnotatedCallLogMigrator { private final Context appContext; private final SharedPreferences sharedPreferences; private final RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker; - private final ListeningExecutorService backgorundExecutor; + private final ListeningExecutorService backgroundExecutor; @Inject AnnotatedCallLogMigrator( @@ -49,7 +50,7 @@ public final class AnnotatedCallLogMigrator { RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker) { this.appContext = appContext; this.sharedPreferences = sharedPreferences; - this.backgorundExecutor = backgroundExecutor; + this.backgroundExecutor = backgroundExecutor; this.refreshAnnotatedCallLogWorker = refreshAnnotatedCallLogWorker; } @@ -58,13 +59,13 @@ public final class AnnotatedCallLogMigrator { * the latency the first time call log is shown. */ public ListenableFuture<Void> migrate() { - return Futures.transformAsync( shouldMigrate(), (shouldMigrate) -> { if (!shouldMigrate) { return Futures.immediateFuture(null); } + LogUtil.i("AnnotatedCallLogMigrator.migrate", "migrating annotated call log"); return Futures.transform( refreshAnnotatedCallLogWorker.refreshWithoutDirtyCheck(), (unused) -> { @@ -77,7 +78,7 @@ public final class AnnotatedCallLogMigrator { } private ListenableFuture<Boolean> shouldMigrate() { - return backgorundExecutor.submit( + return backgroundExecutor.submit( () -> { if (!(ConfigProviderBindings.get(appContext) .getBoolean("is_nui_shortcut_enabled", false))) { diff --git a/java/com/android/dialer/calllog/CallLogState.java b/java/com/android/dialer/calllog/CallLogState.java new file mode 100644 index 000000000..bdb30a769 --- /dev/null +++ b/java/com/android/dialer/calllog/CallLogState.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.dialer.calllog; + +import android.content.SharedPreferences; +import android.support.annotation.AnyThread; +import android.support.annotation.VisibleForTesting; +import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor; +import com.android.dialer.storage.Unencrypted; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import javax.annotation.concurrent.ThreadSafe; +import javax.inject.Inject; + +/** Provides information about the state of the annotated call log. */ +@ThreadSafe +public final class CallLogState { + + private static final String ANNOTATED_CALL_LOG_BUILT_PREF = "annotated_call_log_built"; + + private final SharedPreferences sharedPreferences; + private final ListeningExecutorService backgroundExecutor; + + @VisibleForTesting + @Inject + public CallLogState( + @Unencrypted SharedPreferences sharedPreferences, + @BackgroundExecutor ListeningExecutorService backgroundExecutor) { + this.sharedPreferences = sharedPreferences; + this.backgroundExecutor = backgroundExecutor; + } + + /** + * Mark the call log as having been built. This is written to disk the first time the annotated + * call log has been built and shouldn't ever be reset unless the user clears data. + */ + @AnyThread + public void markBuilt() { + sharedPreferences.edit().putBoolean(ANNOTATED_CALL_LOG_BUILT_PREF, true).apply(); + } + + /** + * Returns true if the annotated call log has been built at least once. + * + * <p>It may not yet have been built if the user was just upgraded to the new call log, or they + * just cleared data. + */ + @AnyThread + public ListenableFuture<Boolean> isBuilt() { + return backgroundExecutor.submit( + () -> sharedPreferences.getBoolean(ANNOTATED_CALL_LOG_BUILT_PREF, false)); + } +} diff --git a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java index 3765831ae..e05b77268 100644 --- a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java +++ b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java @@ -37,6 +37,7 @@ import com.google.common.base.Preconditions; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; @@ -51,6 +52,7 @@ public class RefreshAnnotatedCallLogWorker { private final SharedPreferences sharedPreferences; private final MutationApplier mutationApplier; private final FutureTimer futureTimer; + private final CallLogState callLogState; private final ListeningExecutorService backgroundExecutorService; private final ListeningExecutorService lightweightExecutorService; // Used to ensure that only one refresh flow runs at a time. (Note that @@ -64,6 +66,7 @@ public class RefreshAnnotatedCallLogWorker { @Unencrypted SharedPreferences sharedPreferences, MutationApplier mutationApplier, FutureTimer futureTimer, + CallLogState callLogState, @BackgroundExecutor ListeningExecutorService backgroundExecutorService, @LightweightExecutor ListeningExecutorService lightweightExecutorService) { this.appContext = appContext; @@ -71,17 +74,18 @@ public class RefreshAnnotatedCallLogWorker { this.sharedPreferences = sharedPreferences; this.mutationApplier = mutationApplier; this.futureTimer = futureTimer; + this.callLogState = callLogState; this.backgroundExecutorService = backgroundExecutorService; this.lightweightExecutorService = lightweightExecutorService; } /** Checks if the annotated call log is dirty and refreshes it if necessary. */ - public ListenableFuture<Void> refreshWithDirtyCheck() { + ListenableFuture<Void> refreshWithDirtyCheck() { return refresh(true); } /** Refreshes the annotated call log, bypassing dirty checks. */ - public ListenableFuture<Void> refreshWithoutDirtyCheck() { + ListenableFuture<Void> refreshWithoutDirtyCheck() { return refresh(false); } @@ -131,7 +135,11 @@ public class RefreshAnnotatedCallLogWorker { "RefreshAnnotatedCallLogWorker.checkDirtyAndRebuildIfNecessary", "isDirty: %b", Preconditions.checkNotNull(isDirty)); - return isDirty ? rebuild() : Futures.immediateFuture(null); + if (isDirty) { + return Futures.transformAsync( + callLogState.isBuilt(), this::rebuild, MoreExecutors.directExecutor()); + } + return Futures.immediateFuture(null); }, lightweightExecutorService); } @@ -152,14 +160,13 @@ public class RefreshAnnotatedCallLogWorker { return isDirtyFuture; } - private ListenableFuture<Void> rebuild() { + private ListenableFuture<Void> rebuild(boolean isBuilt) { CallLogMutations mutations = new CallLogMutations(); // Start by filling the data sources--the system call log data source must go first! CallLogDataSource systemCallLogDataSource = dataSources.getSystemCallLogDataSource(); ListenableFuture<Void> fillFuture = systemCallLogDataSource.fill(appContext, mutations); - String systemEventName = - String.format(Metrics.FILL_TEMPLATE, systemCallLogDataSource.getClass().getSimpleName()); + String systemEventName = eventNameForFill(systemCallLogDataSource, isBuilt); futureTimer.applyTiming(fillFuture, systemEventName); // After the system call log data source is filled, call fill sequentially on each remaining @@ -171,14 +178,14 @@ public class RefreshAnnotatedCallLogWorker { fillFuture, unused -> { ListenableFuture<Void> dataSourceFuture = dataSource.fill(appContext, mutations); - String eventName = - String.format(Metrics.FILL_TEMPLATE, dataSource.getClass().getSimpleName()); + String eventName = eventNameForFill(dataSource, isBuilt); futureTimer.applyTiming(dataSourceFuture, eventName); return dataSourceFuture; }, lightweightExecutorService); } - futureTimer.applyTiming(fillFuture, Metrics.FILL_EVENT_NAME); + + futureTimer.applyTiming(fillFuture, eventNameForOverallFill(isBuilt)); // After all data sources are filled, apply mutations (at this point "fillFuture" is the result // of filling the last data source). @@ -188,7 +195,7 @@ public class RefreshAnnotatedCallLogWorker { unused -> { ListenableFuture<Void> mutationApplierFuture = mutationApplier.applyToDatabase(mutations, appContext); - futureTimer.applyTiming(mutationApplierFuture, Metrics.APPLY_MUTATIONS_EVENT_NAME); + futureTimer.applyTiming(mutationApplierFuture, eventNameForApplyMutations(isBuilt)); return mutationApplierFuture; }, lightweightExecutorService); @@ -203,13 +210,11 @@ public class RefreshAnnotatedCallLogWorker { dataSources.getDataSourcesIncludingSystemCallLog()) { ListenableFuture<Void> dataSourceFuture = dataSource.onSuccessfulFill(appContext); onSuccessfulFillFutures.add(dataSourceFuture); - String eventName = - String.format( - Metrics.ON_SUCCESSFUL_FILL_TEMPLATE, dataSource.getClass().getSimpleName()); + String eventName = eventNameForOnSuccessfulFill(dataSource, isBuilt); futureTimer.applyTiming(dataSourceFuture, eventName); } ListenableFuture<List<Void>> allFutures = Futures.allAsList(onSuccessfulFillFutures); - futureTimer.applyTiming(allFutures, Metrics.ON_SUCCESSFUL_FILL_EVENT_NAME); + futureTimer.applyTiming(allFutures, eventNameForOverallOnSuccessfulFill(isBuilt)); return allFutures; }, lightweightExecutorService); @@ -219,8 +224,40 @@ public class RefreshAnnotatedCallLogWorker { onSuccessfulFillFuture, unused -> { sharedPreferences.edit().putBoolean(SharedPrefKeys.FORCE_REBUILD, false).apply(); + callLogState.markBuilt(); return null; }, backgroundExecutorService); } + + private static String eventNameForFill(CallLogDataSource dataSource, boolean isBuilt) { + return String.format( + !isBuilt ? Metrics.INITIAL_FILL_TEMPLATE : Metrics.FILL_TEMPLATE, + dataSource.getClass().getSimpleName()); + } + + private static String eventNameForOverallFill(boolean isBuilt) { + return !isBuilt ? Metrics.INITIAL_FILL_EVENT_NAME : Metrics.FILL_EVENT_NAME; + } + + private static String eventNameForOnSuccessfulFill( + CallLogDataSource dataSource, boolean isBuilt) { + return String.format( + !isBuilt + ? Metrics.INITIAL_ON_SUCCESSFUL_FILL_TEMPLATE + : Metrics.ON_SUCCESSFUL_FILL_TEMPLATE, + dataSource.getClass().getSimpleName()); + } + + private static String eventNameForOverallOnSuccessfulFill(boolean isBuilt) { + return !isBuilt + ? Metrics.INITIAL_ON_SUCCESSFUL_FILL_EVENT_NAME + : Metrics.ON_SUCCESSFUL_FILL_EVENT_NAME; + } + + private static String eventNameForApplyMutations(boolean isBuilt) { + return !isBuilt + ? Metrics.INITIAL_APPLY_MUTATIONS_EVENT_NAME + : Metrics.APPLY_MUTATIONS_EVENT_NAME; + } } diff --git a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java index 40788f42a..ff8c931ad 100644 --- a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java +++ b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java @@ -39,6 +39,7 @@ import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor; import com.android.dialer.common.concurrent.Annotations.LightweightExecutor; import com.android.dialer.phonelookup.PhoneLookup; import com.android.dialer.phonelookup.PhoneLookupInfo; +import com.android.dialer.phonelookup.composite.CompositePhoneLookup; import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract; import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory; import com.google.common.collect.ImmutableMap; @@ -63,7 +64,7 @@ import javax.inject.Inject; */ public final class PhoneLookupDataSource implements CallLogDataSource { - private final PhoneLookup<PhoneLookupInfo> phoneLookup; + private final CompositePhoneLookup compositePhoneLookup; private final ListeningExecutorService backgroundExecutorService; private final ListeningExecutorService lightweightExecutorService; @@ -86,10 +87,10 @@ public final class PhoneLookupDataSource implements CallLogDataSource { @Inject PhoneLookupDataSource( - PhoneLookup<PhoneLookupInfo> phoneLookup, + CompositePhoneLookup compositePhoneLookup, @BackgroundExecutor ListeningExecutorService backgroundExecutorService, @LightweightExecutor ListeningExecutorService lightweightExecutorService) { - this.phoneLookup = phoneLookup; + this.compositePhoneLookup = compositePhoneLookup; this.backgroundExecutorService = backgroundExecutorService; this.lightweightExecutorService = lightweightExecutorService; } @@ -99,7 +100,8 @@ public final class PhoneLookupDataSource implements CallLogDataSource { ListenableFuture<ImmutableSet<DialerPhoneNumber>> phoneNumbers = backgroundExecutorService.submit( () -> queryDistinctDialerPhoneNumbersFromAnnotatedCallLog(appContext)); - return Futures.transformAsync(phoneNumbers, phoneLookup::isDirty, lightweightExecutorService); + return Futures.transformAsync( + phoneNumbers, compositePhoneLookup::isDirty, lightweightExecutorService); } /** @@ -157,10 +159,13 @@ public final class PhoneLookupDataSource implements CallLogDataSource { queryPhoneLookupHistoryForNumbers(appContext, annotatedCallLogIdsByNumber.keySet()), backgroundExecutorService); - // Use the original info map to generate the updated info map by delegating to phoneLookup. + // Use the original info map to generate the updated info map by delegating to + // compositePhoneLookup. ListenableFuture<ImmutableMap<DialerPhoneNumber, PhoneLookupInfo>> updatedInfoMapFuture = Futures.transformAsync( - originalInfoMapFuture, phoneLookup::getMostRecentInfo, lightweightExecutorService); + originalInfoMapFuture, + compositePhoneLookup::getMostRecentInfo, + lightweightExecutorService); // This is the computation that will use the result of all of the above. Callable<ImmutableMap<Long, PhoneLookupInfo>> computeRowsToUpdate = @@ -241,7 +246,7 @@ public final class PhoneLookupDataSource implements CallLogDataSource { // the AnnotatedCallLog and PhoneLookupHistory have been successfully updated. return Futures.transformAsync( writePhoneLookupHistory, - unused -> phoneLookup.onSuccessfulBulkUpdate(), + unused -> compositePhoneLookup.onSuccessfulBulkUpdate(), lightweightExecutorService); } @@ -286,7 +291,7 @@ public final class PhoneLookupDataSource implements CallLogDataSource { @MainThread @Override public void registerContentObservers(Context appContext) { - phoneLookup.registerContentObservers(appContext); + compositePhoneLookup.registerContentObservers(appContext); } private static ImmutableSet<DialerPhoneNumber> diff --git a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java index 69c431953..22c3a3c11 100644 --- a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java +++ b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java @@ -31,8 +31,8 @@ import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor; import com.android.dialer.common.concurrent.Annotations.Ui; import com.android.dialer.common.concurrent.ThreadUtil; import com.android.dialer.inject.ApplicationContext; -import com.android.dialer.phonelookup.PhoneLookup; import com.android.dialer.phonelookup.PhoneLookupInfo; +import com.android.dialer.phonelookup.composite.CompositePhoneLookup; import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract; import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory; import com.google.common.collect.ImmutableMap; @@ -68,7 +68,7 @@ public final class RealtimeRowProcessor { @VisibleForTesting static final long BATCH_WAIT_MILLIS = TimeUnit.SECONDS.toMillis(3); private final Context appContext; - private final PhoneLookup<PhoneLookupInfo> phoneLookup; + private final CompositePhoneLookup compositePhoneLookup; private final ListeningExecutorService uiExecutor; private final ListeningExecutorService backgroundExecutor; @@ -83,11 +83,11 @@ public final class RealtimeRowProcessor { @ApplicationContext Context appContext, @Ui ListeningExecutorService uiExecutor, @BackgroundExecutor ListeningExecutorService backgroundExecutor, - PhoneLookup<PhoneLookupInfo> phoneLookup) { + CompositePhoneLookup compositePhoneLookup) { this.appContext = appContext; this.uiExecutor = uiExecutor; this.backgroundExecutor = backgroundExecutor; - this.phoneLookup = phoneLookup; + this.compositePhoneLookup = compositePhoneLookup; } /** @@ -106,7 +106,8 @@ public final class RealtimeRowProcessor { return Futures.immediateFuture(applyPhoneLookupInfoToRow(cachedPhoneLookupInfo, row)); } - ListenableFuture<PhoneLookupInfo> phoneLookupInfoFuture = phoneLookup.lookup(row.number()); + ListenableFuture<PhoneLookupInfo> phoneLookupInfoFuture = + compositePhoneLookup.lookup(row.number()); return Futures.transform( phoneLookupInfoFuture, phoneLookupInfo -> { |