summaryrefslogtreecommitdiff
path: root/java/com/android/dialer
diff options
context:
space:
mode:
authorlinyuh <linyuh@google.com>2018-02-20 12:46:54 -0800
committerEric Erfanian <erfanian@google.com>2018-02-22 21:08:26 +0000
commit43c978b616363bcd364693dde24209384b264319 (patch)
tree937f001e840973720fa567970b013fff03af2291 /java/com/android/dialer
parente2bf3e9d45aa8c1a6dbf39212f7ba32cbbd53185 (diff)
Use a broadcast receiver to refresh the annotated call log.
Bug: 73347270 Test: Existing tests + RefreshAnnotatedCallLogNotifierTest PiperOrigin-RevId: 186347066 Change-Id: I5a530416bdaa9edc7131a0d5ced44f1b5ee1692b
Diffstat (limited to 'java/com/android/dialer')
-rw-r--r--java/com/android/dialer/calllog/CallLogComponent.java3
-rw-r--r--java/com/android/dialer/calllog/CallLogFramework.java70
-rw-r--r--java/com/android/dialer/calllog/RefreshAnnotatedCallLogReceiver.java121
-rw-r--r--java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java5
-rw-r--r--java/com/android/dialer/calllog/constants/IntentNames.java30
-rw-r--r--java/com/android/dialer/calllog/constants/SharedPrefKeys.java25
-rw-r--r--java/com/android/dialer/calllog/datasources/CallLogDataSource.java11
-rw-r--r--java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java18
-rw-r--r--java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java23
-rw-r--r--java/com/android/dialer/calllog/notifier/RefreshAnnotatedCallLogNotifier.java99
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogFragment.java93
-rw-r--r--java/com/android/dialer/phonelookup/PhoneLookup.java11
-rw-r--r--java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java10
-rw-r--r--java/com/android/dialer/phonelookup/blockednumber/MarkDirtyObserver.java21
-rw-r--r--java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java10
-rw-r--r--java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java5
-rw-r--r--java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java3
-rw-r--r--java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java3
-rw-r--r--java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java6
-rw-r--r--java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java107
20 files changed, 387 insertions, 287 deletions
diff --git a/java/com/android/dialer/calllog/CallLogComponent.java b/java/com/android/dialer/calllog/CallLogComponent.java
index bb5bfee2a..4f147f1a6 100644
--- a/java/com/android/dialer/calllog/CallLogComponent.java
+++ b/java/com/android/dialer/calllog/CallLogComponent.java
@@ -16,6 +16,7 @@
package com.android.dialer.calllog;
import android.content.Context;
+import com.android.dialer.calllog.notifier.RefreshAnnotatedCallLogNotifier;
import com.android.dialer.inject.HasRootComponent;
import dagger.Subcomponent;
@@ -25,6 +26,8 @@ public abstract class CallLogComponent {
public abstract CallLogFramework callLogFramework();
+ public abstract RefreshAnnotatedCallLogNotifier getRefreshAnnotatedCallLogNotifier();
+
public abstract RefreshAnnotatedCallLogWorker getRefreshAnnotatedCallLogWorker();
public abstract ClearMissedCalls getClearMissedCalls();
diff --git a/java/com/android/dialer/calllog/CallLogFramework.java b/java/com/android/dialer/calllog/CallLogFramework.java
index 440055de6..7da8d9c0c 100644
--- a/java/com/android/dialer/calllog/CallLogFramework.java
+++ b/java/com/android/dialer/calllog/CallLogFramework.java
@@ -17,40 +17,26 @@
package com.android.dialer.calllog;
import android.content.Context;
-import android.content.SharedPreferences;
-import android.support.annotation.MainThread;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
import com.android.dialer.calllog.datasources.CallLogDataSource;
import com.android.dialer.calllog.datasources.DataSources;
-import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.configprovider.ConfigProviderBindings;
-import com.android.dialer.storage.Unencrypted;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
- * Coordinates work across CallLog data sources to detect if the annotated call log is out of date
- * ("dirty") and update it if necessary.
+ * Coordinates work across {@link DataSources}.
*
* <p>All methods should be called on the main thread.
*/
@Singleton
-public final class CallLogFramework implements CallLogDataSource.ContentObserverCallbacks {
-
- @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
- public static final String PREF_FORCE_REBUILD = "callLogFrameworkForceRebuild";
+public final class CallLogFramework {
private final DataSources dataSources;
- private final SharedPreferences sharedPreferences;
-
- @Nullable private CallLogUi ui;
@Inject
- CallLogFramework(DataSources dataSources, @Unencrypted SharedPreferences sharedPreferences) {
+ CallLogFramework(DataSources dataSources) {
this.dataSources = dataSources;
- this.sharedPreferences = sharedPreferences;
}
/** Registers the content observers for all data sources. */
@@ -63,58 +49,10 @@ public final class CallLogFramework implements CallLogDataSource.ContentObserver
// TODO(zachh): Find a way to access Main#isNewUiEnabled without creating a circular dependency.
if (ConfigProviderBindings.get(appContext).getBoolean("is_nui_shortcut_enabled", false)) {
for (CallLogDataSource dataSource : dataSources.getDataSourcesIncludingSystemCallLog()) {
- dataSource.registerContentObservers(appContext, this);
+ dataSource.registerContentObservers(appContext);
}
} else {
LogUtil.i("CallLogFramework.registerContentObservers", "not registering content observers");
}
}
-
- /**
- * Attach a UI component to the framework so that it may be notified of changes to the annotated
- * call log.
- */
- public void attachUi(CallLogUi ui) {
- LogUtil.enterBlock("CallLogFramework.attachUi");
- this.ui = ui;
- }
-
- /**
- * Detaches the UI from the framework. This should be called when the UI is hidden or destroyed
- * and no longer needs to be notified of changes to the annotated call log.
- */
- public void detachUi() {
- LogUtil.enterBlock("CallLogFramework.detachUi");
- this.ui = null;
- }
-
- /**
- * Marks the call log as dirty and notifies any attached UI components. If there are no UI
- * components currently attached, this is an efficient operation since it is just writing a shared
- * pref.
- *
- * <p>We don't want to actually force a rebuild when there is no UI running because we don't want
- * to be constantly rebuilding the database when the device is sitting on a desk and receiving a
- * lot of calls, for example.
- */
- @Override
- @MainThread
- public void markDirtyAndNotify(Context appContext) {
- Assert.isMainThread();
- LogUtil.enterBlock("CallLogFramework.markDirtyAndNotify");
-
- sharedPreferences.edit().putBoolean(PREF_FORCE_REBUILD, true).apply();
-
- if (ui != null) {
- ui.invalidateUi();
- }
- }
-
- /** Callbacks invoked on listening UI components. */
- public interface CallLogUi {
-
- /** Notifies the call log UI that the annotated call log is out of date. */
- @MainThread
- void invalidateUi();
- }
}
diff --git a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogReceiver.java b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogReceiver.java
new file mode 100644
index 000000000..e0bfcd8a3
--- /dev/null
+++ b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogReceiver.java
@@ -0,0 +1,121 @@
+/*
+ * 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.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.support.annotation.Nullable;
+import com.android.dialer.calllog.constants.IntentNames;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.DefaultFutureCallback;
+import com.android.dialer.common.concurrent.ThreadUtil;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+
+/**
+ * A {@link BroadcastReceiver} that starts/cancels refreshing the annotated call log when notified.
+ */
+public final class RefreshAnnotatedCallLogReceiver extends BroadcastReceiver {
+
+ /**
+ * This is a reasonable time that it might take between related call log writes, that also
+ * shouldn't slow down single-writes too much. For example, when populating the database using the
+ * simulator, using this value results in ~6 refresh cycles (on a release build) to write 120 call
+ * log entries.
+ */
+ private static final long REFRESH_ANNOTATED_CALL_LOG_WAIT_MILLIS = 100L;
+
+ private final RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker;
+
+ @Nullable private Runnable refreshAnnotatedCallLogRunnable;
+
+ /** Returns an {@link IntentFilter} containing all actions accepted by this broadcast receiver. */
+ public static IntentFilter getIntentFilter() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(IntentNames.ACTION_REFRESH_ANNOTATED_CALL_LOG);
+ intentFilter.addAction(IntentNames.ACTION_CANCEL_REFRESHING_ANNOTATED_CALL_LOG);
+ return intentFilter;
+ }
+
+ public RefreshAnnotatedCallLogReceiver(Context context) {
+ refreshAnnotatedCallLogWorker =
+ CallLogComponent.get(context).getRefreshAnnotatedCallLogWorker();
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ LogUtil.enterBlock("RefreshAnnotatedCallLogReceiver.onReceive");
+
+ String action = intent.getAction();
+
+ if (IntentNames.ACTION_REFRESH_ANNOTATED_CALL_LOG.equals(action)) {
+ boolean checkDirty = intent.getBooleanExtra(IntentNames.EXTRA_CHECK_DIRTY, false);
+ refreshAnnotatedCallLog(checkDirty);
+ } else if (IntentNames.ACTION_CANCEL_REFRESHING_ANNOTATED_CALL_LOG.equals(action)) {
+ cancelRefreshingAnnotatedCallLog();
+ }
+ }
+
+ /**
+ * Request a refresh of the annotated call log.
+ *
+ * <p>Note that the execution will be delayed by {@link #REFRESH_ANNOTATED_CALL_LOG_WAIT_MILLIS}.
+ * Once the work begins, it can't be cancelled.
+ *
+ * @see #cancelRefreshingAnnotatedCallLog()
+ */
+ private void refreshAnnotatedCallLog(boolean checkDirty) {
+ LogUtil.enterBlock("RefreshAnnotatedCallLogReceiver.refreshAnnotatedCallLog");
+
+ // If we already scheduled a refresh, cancel it and schedule a new one so that repeated requests
+ // in quick succession don't result in too much work. For example, if we get 10 requests in
+ // 10ms, and a complete refresh takes a constant 200ms, the refresh will take 300ms (100ms wait
+ // and 1 iteration @200ms) instead of 2 seconds (10 iterations @ 200ms) since the work requests
+ // are serialized in RefreshAnnotatedCallLogWorker.
+ //
+ // We might get many requests in quick succession, for example, when the simulator inserts
+ // hundreds of rows into the system call log, or when the data for a new call is incrementally
+ // written to different columns as it becomes available.
+ ThreadUtil.getUiThreadHandler().removeCallbacks(refreshAnnotatedCallLogRunnable);
+
+ refreshAnnotatedCallLogRunnable =
+ () -> {
+ ListenableFuture<Void> future =
+ checkDirty
+ ? refreshAnnotatedCallLogWorker.refreshWithDirtyCheck()
+ : refreshAnnotatedCallLogWorker.refreshWithoutDirtyCheck();
+ Futures.addCallback(
+ future, new DefaultFutureCallback<>(), MoreExecutors.directExecutor());
+ };
+
+ ThreadUtil.getUiThreadHandler()
+ .postDelayed(refreshAnnotatedCallLogRunnable, REFRESH_ANNOTATED_CALL_LOG_WAIT_MILLIS);
+ }
+
+ /**
+ * When a refresh is requested, its execution is delayed (see {@link
+ * #refreshAnnotatedCallLog(boolean)}). This method only cancels the refresh if it hasn't started.
+ */
+ private void cancelRefreshingAnnotatedCallLog() {
+ LogUtil.enterBlock("RefreshAnnotatedCallLogReceiver.cancelRefreshingAnnotatedCallLog");
+
+ ThreadUtil.getUiThreadHandler().removeCallbacks(refreshAnnotatedCallLogRunnable);
+ }
+}
diff --git a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
index 4c5904ef1..a430d14a8 100644
--- a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
+++ b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
@@ -18,6 +18,7 @@ package com.android.dialer.calllog;
import android.content.Context;
import android.content.SharedPreferences;
+import com.android.dialer.calllog.constants.SharedPrefKeys;
import com.android.dialer.calllog.database.CallLogDatabaseComponent;
import com.android.dialer.calllog.datasources.CallLogDataSource;
import com.android.dialer.calllog.datasources.CallLogMutations;
@@ -95,7 +96,7 @@ public class RefreshAnnotatedCallLogWorker {
// Default to true. If the pref doesn't exist, the annotated call log hasn't been
// created and we just skip isDirty checks and force a rebuild.
boolean forceRebuildPrefValue =
- sharedPreferences.getBoolean(CallLogFramework.PREF_FORCE_REBUILD, true);
+ sharedPreferences.getBoolean(SharedPrefKeys.FORCE_REBUILD, true);
if (forceRebuildPrefValue) {
LogUtil.i(
"RefreshAnnotatedCallLogWorker.checkDirtyAndRebuildIfNecessary",
@@ -183,7 +184,7 @@ public class RefreshAnnotatedCallLogWorker {
return Futures.transform(
onSuccessfulFillFuture,
unused -> {
- sharedPreferences.edit().putBoolean(CallLogFramework.PREF_FORCE_REBUILD, false).apply();
+ sharedPreferences.edit().putBoolean(SharedPrefKeys.FORCE_REBUILD, false).apply();
return null;
},
backgroundExecutorService);
diff --git a/java/com/android/dialer/calllog/constants/IntentNames.java b/java/com/android/dialer/calllog/constants/IntentNames.java
new file mode 100644
index 000000000..3912450a1
--- /dev/null
+++ b/java/com/android/dialer/calllog/constants/IntentNames.java
@@ -0,0 +1,30 @@
+/*
+ * 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.constants;
+
+/** A class containing names for call log intents. */
+public final class IntentNames {
+
+ public static final String ACTION_REFRESH_ANNOTATED_CALL_LOG = "refresh_annotated_call_log";
+
+ public static final String ACTION_CANCEL_REFRESHING_ANNOTATED_CALL_LOG =
+ "cancel_refreshing_annotated_call_log";
+
+ public static final String EXTRA_CHECK_DIRTY = "check_dirty";
+
+ private IntentNames() {}
+}
diff --git a/java/com/android/dialer/calllog/constants/SharedPrefKeys.java b/java/com/android/dialer/calllog/constants/SharedPrefKeys.java
new file mode 100644
index 000000000..41e65bb53
--- /dev/null
+++ b/java/com/android/dialer/calllog/constants/SharedPrefKeys.java
@@ -0,0 +1,25 @@
+/*
+ * 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.constants;
+
+/** Keys for shared preferences in the call log package. */
+public final class SharedPrefKeys {
+
+ public static final String FORCE_REBUILD = "force_rebuild";
+
+ private SharedPrefKeys() {}
+}
diff --git a/java/com/android/dialer/calllog/datasources/CallLogDataSource.java b/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
index 60654a81a..dbed1d81c 100644
--- a/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
@@ -104,14 +104,5 @@ public interface CallLogDataSource {
ContentValues coalesce(List<ContentValues> individualRowsSortedByTimestampDesc);
@MainThread
- void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks);
-
- /**
- * Methods which may optionally be called as a result of a data source's content observer firing.
- */
- interface ContentObserverCallbacks {
- @MainThread
- void markDirtyAndNotify(Context appContext);
- }
+ void registerContentObservers(Context appContext);
}
diff --git a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
index 8dec43759..40788f42a 100644
--- a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
@@ -61,8 +61,7 @@ import javax.inject.Inject;
* Responsible for maintaining the columns in the annotated call log which are derived from phone
* numbers.
*/
-public final class PhoneLookupDataSource
- implements CallLogDataSource, PhoneLookup.ContentObserverCallbacks {
+public final class PhoneLookupDataSource implements CallLogDataSource {
private final PhoneLookup<PhoneLookupInfo> phoneLookup;
private final ListeningExecutorService backgroundExecutorService;
@@ -85,8 +84,6 @@ public final class PhoneLookupDataSource
*/
private final Set<String> phoneLookupHistoryRowsToDelete = new ArraySet<>();
- private CallLogDataSource.ContentObserverCallbacks dataSourceContentObserverCallbacks;
-
@Inject
PhoneLookupDataSource(
PhoneLookup<PhoneLookupInfo> phoneLookup,
@@ -288,17 +285,8 @@ public final class PhoneLookupDataSource
@MainThread
@Override
- public void registerContentObservers(
- Context appContext, CallLogDataSource.ContentObserverCallbacks contentObserverCallbacks) {
- dataSourceContentObserverCallbacks = contentObserverCallbacks;
- phoneLookup.registerContentObservers(appContext, this);
- }
-
- @MainThread
- @Override
- public void markDirtyAndNotify(Context appContext) {
- Assert.isMainThread();
- dataSourceContentObserverCallbacks.markDirtyAndNotify(appContext);
+ public void registerContentObservers(Context appContext) {
+ phoneLookup.registerContentObservers(appContext);
}
private static ImmutableSet<DialerPhoneNumber>
diff --git a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
index ee484d95e..e9f7c00bf 100644
--- a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
@@ -47,6 +47,7 @@ import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.Ann
import com.android.dialer.calllog.datasources.CallLogDataSource;
import com.android.dialer.calllog.datasources.CallLogMutations;
import com.android.dialer.calllog.datasources.util.RowCombiner;
+import com.android.dialer.calllog.notifier.RefreshAnnotatedCallLogNotifier;
import com.android.dialer.calllogutils.PhoneAccountUtils;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
@@ -79,18 +80,21 @@ public class SystemCallLogDataSource implements CallLogDataSource {
static final String PREF_LAST_TIMESTAMP_PROCESSED = "systemCallLogLastTimestampProcessed";
private final ListeningExecutorService backgroundExecutorService;
+ private final RefreshAnnotatedCallLogNotifier refreshAnnotatedCallLogNotifier;
@Nullable private Long lastTimestampProcessed;
@Inject
- SystemCallLogDataSource(@BackgroundExecutor ListeningExecutorService backgroundExecutorService) {
+ SystemCallLogDataSource(
+ @BackgroundExecutor ListeningExecutorService backgroundExecutorService,
+ RefreshAnnotatedCallLogNotifier refreshAnnotatedCallLogNotifier) {
this.backgroundExecutorService = backgroundExecutorService;
+ this.refreshAnnotatedCallLogNotifier = refreshAnnotatedCallLogNotifier;
}
@MainThread
@Override
- public void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ public void registerContentObservers(Context appContext) {
Assert.isMainThread();
LogUtil.enterBlock("SystemCallLogDataSource.registerContentObservers");
@@ -102,7 +106,7 @@ public class SystemCallLogDataSource implements CallLogDataSource {
// TODO(zachh): Need to somehow register observers if user enables permission after launch?
CallLogObserver callLogObserver =
- new CallLogObserver(ThreadUtil.getUiThreadHandler(), appContext, contentObserverCallbacks);
+ new CallLogObserver(ThreadUtil.getUiThreadHandler(), refreshAnnotatedCallLogNotifier);
appContext
.getContentResolver()
@@ -524,15 +528,14 @@ public class SystemCallLogDataSource implements CallLogDataSource {
return ids;
}
+ // TODO(a bug): Consider replacing it with MarkDirtyObserver.
private static class CallLogObserver extends ContentObserver {
- private final Context appContext;
- private final ContentObserverCallbacks contentObserverCallbacks;
+ private final RefreshAnnotatedCallLogNotifier refreshAnnotatedCallLogNotifier;
CallLogObserver(
- Handler handler, Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ Handler handler, RefreshAnnotatedCallLogNotifier refreshAnnotatedCallLogNotifier) {
super(handler);
- this.appContext = appContext;
- this.contentObserverCallbacks = contentObserverCallbacks;
+ this.refreshAnnotatedCallLogNotifier = refreshAnnotatedCallLogNotifier;
}
@MainThread
@@ -552,7 +555,7 @@ public class SystemCallLogDataSource implements CallLogDataSource {
* table, which would be too slow. So, we just rely on content observers to trigger rebuilds
* when any change is made to the system call log.
*/
- contentObserverCallbacks.markDirtyAndNotify(appContext);
+ refreshAnnotatedCallLogNotifier.markDirtyAndNotify();
}
}
}
diff --git a/java/com/android/dialer/calllog/notifier/RefreshAnnotatedCallLogNotifier.java b/java/com/android/dialer/calllog/notifier/RefreshAnnotatedCallLogNotifier.java
new file mode 100644
index 000000000..5b73ad778
--- /dev/null
+++ b/java/com/android/dialer/calllog/notifier/RefreshAnnotatedCallLogNotifier.java
@@ -0,0 +1,99 @@
+/*
+ * 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.notifier;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.support.v4.content.LocalBroadcastManager;
+import com.android.dialer.calllog.constants.IntentNames;
+import com.android.dialer.calllog.constants.SharedPrefKeys;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.inject.ApplicationContext;
+import com.android.dialer.storage.Unencrypted;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Notifies that a refresh of the annotated call log needs to be started/cancelled.
+ *
+ * <p>Methods in this class are usually invoked when the underlying data backing the annotated call
+ * log change.
+ *
+ * <p>For example, a {@link android.database.ContentObserver} for the system call log can use {@link
+ * #markDirtyAndNotify()} to force the annotated call log to be rebuilt.
+ */
+@Singleton
+public class RefreshAnnotatedCallLogNotifier {
+
+ private final Context appContext;
+ private final SharedPreferences sharedPreferences;
+
+ @Inject
+ RefreshAnnotatedCallLogNotifier(
+ @ApplicationContext Context appContext, @Unencrypted SharedPreferences sharedPreferences) {
+ this.appContext = appContext;
+ this.sharedPreferences = sharedPreferences;
+ }
+
+ /**
+ * Mark the annotated call log as "dirty" and notify that it needs to be refreshed.
+ *
+ * <p>This will force a rebuild by skip checking whether the annotated call log is "dirty".
+ */
+ public void markDirtyAndNotify() {
+ LogUtil.enterBlock("RefreshAnnotatedCallLogNotifier.markDirtyAndNotify");
+
+ sharedPreferences.edit().putBoolean(SharedPrefKeys.FORCE_REBUILD, true).apply();
+ notify(/* checkDirty = */ false);
+ }
+
+ /**
+ * Notifies that the annotated call log needs to be refreshed.
+ *
+ * <p>Note that the notification is sent as a broadcast, which means the annotated call log might
+ * not be refreshed if there is no corresponding receiver registered.
+ *
+ * @param checkDirty Whether to check if the annotated call log is "dirty" before proceeding to
+ * rebuild it.
+ */
+ public void notify(boolean checkDirty) {
+ LogUtil.i("RefreshAnnotatedCallLogNotifier.notify", "checkDirty = %s", checkDirty);
+
+ Intent intent = new Intent();
+ intent.setAction(IntentNames.ACTION_REFRESH_ANNOTATED_CALL_LOG);
+ intent.putExtra(IntentNames.EXTRA_CHECK_DIRTY, checkDirty);
+
+ LocalBroadcastManager.getInstance(appContext).sendBroadcast(intent);
+ }
+
+ /**
+ * Notifies to cancel refreshing the annotated call log.
+ *
+ * <p>Note that this method does not guarantee the job to be cancelled. As the notification is
+ * sent as a broadcast, please see the corresponding receiver for details about cancelling the
+ * job.
+ */
+ public void cancel() {
+ LogUtil.enterBlock("RefreshAnnotatedCallLogNotifier.cancel");
+
+ Intent intent = new Intent();
+ intent.setAction(IntentNames.ACTION_CANCEL_REFRESHING_ANNOTATED_CALL_LOG);
+
+ LocalBroadcastManager.getInstance(appContext).sendBroadcast(intent);
+ }
+}
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
index 5e676f072..8e8d55f3a 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
@@ -22,44 +22,29 @@ import android.support.annotation.VisibleForTesting;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
+import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.dialer.calllog.CallLogComponent;
-import com.android.dialer.calllog.CallLogFramework;
-import com.android.dialer.calllog.CallLogFramework.CallLogUi;
-import com.android.dialer.calllog.RefreshAnnotatedCallLogWorker;
+import com.android.dialer.calllog.RefreshAnnotatedCallLogReceiver;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.DefaultFutureCallback;
-import com.android.dialer.common.concurrent.DialerExecutorComponent;
import com.android.dialer.common.concurrent.ThreadUtil;
-import com.android.dialer.common.concurrent.UiListener;
import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.concurrent.TimeUnit;
/** The "new" call log fragment implementation, which is built on top of the annotated call log. */
-public final class NewCallLogFragment extends Fragment
- implements CallLogUi, LoaderCallbacks<Cursor> {
-
- /*
- * This is a reasonable time that it might take between related call log writes, that also
- * shouldn't slow down single-writes too much. For example, when populating the database using
- * the simulator, using this value results in ~6 refresh cycles (on a release build) to write 120
- * call log entries.
- */
- private static final long REFRESH_ANNOTATED_CALL_LOG_WAIT_MILLIS = 100L;
+public final class NewCallLogFragment extends Fragment implements LoaderCallbacks<Cursor> {
@VisibleForTesting
static final long MARK_ALL_CALLS_READ_WAIT_MILLIS = TimeUnit.SECONDS.toMillis(3);
- private RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker;
- private UiListener<Void> refreshAnnotatedCallLogListener;
+ private RefreshAnnotatedCallLogReceiver refreshAnnotatedCallLogReceiver;
private RecyclerView recyclerView;
- @Nullable private Runnable refreshAnnotatedCallLogRunnable;
private boolean shouldMarkCallsRead = false;
private final Runnable setShouldMarkCallsReadTrue = () -> shouldMarkCallsRead = true;
@@ -74,16 +59,7 @@ public final class NewCallLogFragment extends Fragment
LogUtil.enterBlock("NewCallLogFragment.onActivityCreated");
- CallLogComponent component = CallLogComponent.get(getContext());
- CallLogFramework callLogFramework = component.callLogFramework();
- callLogFramework.attachUi(this);
-
- // TODO(zachh): Use support fragment manager and add support for them in executors library.
- refreshAnnotatedCallLogListener =
- DialerExecutorComponent.get(getContext())
- .createUiListener(
- getActivity().getFragmentManager(), "NewCallLogFragment.refreshAnnotatedCallLog");
- refreshAnnotatedCallLogWorker = component.getRefreshAnnotatedCallLogWorker();
+ refreshAnnotatedCallLogReceiver = new RefreshAnnotatedCallLogReceiver(getContext());
}
@Override
@@ -99,11 +75,12 @@ public final class NewCallLogFragment extends Fragment
LogUtil.enterBlock("NewCallLogFragment.onResume");
- CallLogFramework callLogFramework = CallLogComponent.get(getContext()).callLogFramework();
- callLogFramework.attachUi(this);
+ registerRefreshAnnotatedCallLogReceiver();
// TODO(zachh): Consider doing this when fragment becomes visible.
- refreshAnnotatedCallLog(true /* checkDirty */);
+ CallLogComponent.get(getContext())
+ .getRefreshAnnotatedCallLogNotifier()
+ .notify(/* checkDirty = */ true);
// There are some types of data that we show in the call log that are not represented in the
// AnnotatedCallLog. For example, CP2 information for invalid numbers can sometimes only be
@@ -130,11 +107,9 @@ public final class NewCallLogFragment extends Fragment
LogUtil.enterBlock("NewCallLogFragment.onPause");
// This is pending work that we don't actually need to follow through with.
- ThreadUtil.getUiThreadHandler().removeCallbacks(refreshAnnotatedCallLogRunnable);
ThreadUtil.getUiThreadHandler().removeCallbacks(setShouldMarkCallsReadTrue);
- CallLogFramework callLogFramework = CallLogComponent.get(getContext()).callLogFramework();
- callLogFramework.detachUi();
+ unregisterRefreshAnnotatedCallLogReceiver();
if (shouldMarkCallsRead) {
Futures.addCallback(
@@ -157,42 +132,22 @@ public final class NewCallLogFragment extends Fragment
return view;
}
- private void refreshAnnotatedCallLog(boolean checkDirty) {
- LogUtil.enterBlock("NewCallLogFragment.refreshAnnotatedCallLog");
-
- // If we already scheduled a refresh, cancel it and schedule a new one so that repeated requests
- // in quick succession don't result in too much work. For example, if we get 10 requests in
- // 10ms, and a complete refresh takes a constant 200ms, the refresh will take 300ms (100ms wait
- // and 1 iteration @200ms) instead of 2 seconds (10 iterations @ 200ms) since the work requests
- // are serialized in RefreshAnnotatedCallLogWorker.
- //
- // We might get many requests in quick succession, for example, when the simulator inserts
- // hundreds of rows into the system call log, or when the data for a new call is incrementally
- // written to different columns as it becomes available.
- ThreadUtil.getUiThreadHandler().removeCallbacks(refreshAnnotatedCallLogRunnable);
-
- refreshAnnotatedCallLogRunnable =
- () -> {
- ListenableFuture<Void> future =
- checkDirty
- ? refreshAnnotatedCallLogWorker.refreshWithDirtyCheck()
- : refreshAnnotatedCallLogWorker.refreshWithoutDirtyCheck();
- refreshAnnotatedCallLogListener.listen(
- getContext(),
- future,
- unused -> {},
- throwable -> {
- throw new RuntimeException(throwable);
- });
- };
- ThreadUtil.getUiThreadHandler()
- .postDelayed(refreshAnnotatedCallLogRunnable, REFRESH_ANNOTATED_CALL_LOG_WAIT_MILLIS);
+ private void registerRefreshAnnotatedCallLogReceiver() {
+ LogUtil.enterBlock("NewCallLogFragment.registerRefreshAnnotatedCallLogReceiver");
+
+ LocalBroadcastManager.getInstance(getContext())
+ .registerReceiver(
+ refreshAnnotatedCallLogReceiver, RefreshAnnotatedCallLogReceiver.getIntentFilter());
}
- @Override
- public void invalidateUi() {
- LogUtil.enterBlock("NewCallLogFragment.invalidateUi");
- refreshAnnotatedCallLog(false /* checkDirty */);
+ private void unregisterRefreshAnnotatedCallLogReceiver() {
+ LogUtil.enterBlock("NewCallLogFragment.unregisterRefreshAnnotatedCallLogReceiver");
+
+ // Cancel pending work as we don't need it any more.
+ CallLogComponent.get(getContext()).getRefreshAnnotatedCallLogNotifier().cancel();
+
+ LocalBroadcastManager.getInstance(getContext())
+ .unregisterReceiver(refreshAnnotatedCallLogReceiver);
}
@Override
diff --git a/java/com/android/dialer/phonelookup/PhoneLookup.java b/java/com/android/dialer/phonelookup/PhoneLookup.java
index 76ff98e7c..a7974ad10 100644
--- a/java/com/android/dialer/phonelookup/PhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/PhoneLookup.java
@@ -84,14 +84,5 @@ public interface PhoneLookup<T> {
ListenableFuture<Void> onSuccessfulBulkUpdate();
@MainThread
- void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks);
-
- /**
- * Methods which may optionally be called as a result of a phone lookup's content observer firing.
- */
- interface ContentObserverCallbacks {
- @MainThread
- void markDirtyAndNotify(Context appContext);
- }
+ void registerContentObservers(Context appContext);
}
diff --git a/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java b/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java
index 2271c7580..2d019c8c2 100644
--- a/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java
@@ -51,13 +51,16 @@ public final class DialerBlockedNumberPhoneLookup implements PhoneLookup<DialerB
private final Context appContext;
private final ListeningExecutorService executorService;
+ private final MarkDirtyObserver markDirtyObserver;
@Inject
DialerBlockedNumberPhoneLookup(
@ApplicationContext Context appContext,
- @BackgroundExecutor ListeningExecutorService executorService) {
+ @BackgroundExecutor ListeningExecutorService executorService,
+ MarkDirtyObserver markDirtyObserver) {
this.appContext = appContext;
this.executorService = executorService;
+ this.markDirtyObserver = markDirtyObserver;
}
@Override
@@ -165,13 +168,12 @@ public final class DialerBlockedNumberPhoneLookup implements PhoneLookup<DialerB
}
@Override
- public void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ public void registerContentObservers(Context appContext) {
appContext
.getContentResolver()
.registerContentObserver(
FilteredNumber.CONTENT_URI,
true, // FilteredNumberProvider notifies on the item
- new MarkDirtyObserver(appContext, contentObserverCallbacks));
+ markDirtyObserver);
}
}
diff --git a/java/com/android/dialer/phonelookup/blockednumber/MarkDirtyObserver.java b/java/com/android/dialer/phonelookup/blockednumber/MarkDirtyObserver.java
index 1c41d8f7f..9f72ba48b 100644
--- a/java/com/android/dialer/phonelookup/blockednumber/MarkDirtyObserver.java
+++ b/java/com/android/dialer/phonelookup/blockednumber/MarkDirtyObserver.java
@@ -16,25 +16,28 @@
package com.android.dialer.phonelookup.blockednumber;
-import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.support.annotation.MainThread;
+import com.android.dialer.calllog.notifier.RefreshAnnotatedCallLogNotifier;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.ThreadUtil;
-import com.android.dialer.phonelookup.PhoneLookup.ContentObserverCallbacks;
+import javax.inject.Inject;
-/** Calls {@link ContentObserverCallbacks#markDirtyAndNotify(Context)} when the content changed */
+/**
+ * Mark the annotated call log as dirty and notify that a refresh is in order when the content
+ * changes.
+ */
+// TODO(a bug): Consider making this class available to all data sources and PhoneLookups.
class MarkDirtyObserver extends ContentObserver {
- private final Context appContext;
- private final ContentObserverCallbacks contentObserverCallbacks;
+ private final RefreshAnnotatedCallLogNotifier refreshAnnotatedCallLogNotifier;
- MarkDirtyObserver(Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ @Inject
+ MarkDirtyObserver(RefreshAnnotatedCallLogNotifier refreshAnnotatedCallLogNotifier) {
super(ThreadUtil.getUiThreadHandler());
- this.appContext = appContext;
- this.contentObserverCallbacks = contentObserverCallbacks;
+ this.refreshAnnotatedCallLogNotifier = refreshAnnotatedCallLogNotifier;
}
@MainThread
@@ -42,6 +45,6 @@ class MarkDirtyObserver extends ContentObserver {
public void onChange(boolean selfChange, Uri uri) {
Assert.isMainThread();
LogUtil.enterBlock("SystemBlockedNumberPhoneLookup.FilteredNumberObserver.onChange");
- contentObserverCallbacks.markDirtyAndNotify(appContext);
+ refreshAnnotatedCallLogNotifier.markDirtyAndNotify();
}
}
diff --git a/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java b/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java
index e0ff995e7..f35b3e131 100644
--- a/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java
@@ -54,13 +54,16 @@ public class SystemBlockedNumberPhoneLookup implements PhoneLookup<SystemBlocked
private final Context appContext;
private final ListeningExecutorService executorService;
+ private final MarkDirtyObserver markDirtyObserver;
@Inject
SystemBlockedNumberPhoneLookup(
@ApplicationContext Context appContext,
- @BackgroundExecutor ListeningExecutorService executorService) {
+ @BackgroundExecutor ListeningExecutorService executorService,
+ MarkDirtyObserver markDirtyObserver) {
this.appContext = appContext;
this.executorService = executorService;
+ this.markDirtyObserver = markDirtyObserver;
}
@Override
@@ -166,8 +169,7 @@ public class SystemBlockedNumberPhoneLookup implements PhoneLookup<SystemBlocked
}
@Override
- public void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ public void registerContentObservers(Context appContext) {
if (VERSION.SDK_INT < VERSION_CODES.N) {
return;
}
@@ -176,6 +178,6 @@ public class SystemBlockedNumberPhoneLookup implements PhoneLookup<SystemBlocked
.registerContentObserver(
BlockedNumbers.CONTENT_URI,
true, // BlockedNumbers notifies on the item
- new MarkDirtyObserver(appContext, contentObserverCallbacks));
+ markDirtyObserver);
}
}
diff --git a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
index b77a86ca0..abe18f7dc 100644
--- a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
@@ -179,10 +179,9 @@ public final class CompositePhoneLookup implements PhoneLookup<PhoneLookupInfo>
@Override
@MainThread
- public void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ public void registerContentObservers(Context appContext) {
for (PhoneLookup phoneLookup : phoneLookups) {
- phoneLookup.registerContentObservers(appContext, contentObserverCallbacks);
+ phoneLookup.registerContentObservers(appContext);
}
}
}
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
index e051f473c..8db308892 100644
--- a/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
@@ -619,8 +619,7 @@ public final class Cp2LocalPhoneLookup implements PhoneLookup<Cp2Info> {
}
@Override
- public void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ public void registerContentObservers(Context appContext) {
// Do nothing since CP2 changes are too noisy.
}
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java
index cc4fbf19f..7efe039eb 100644
--- a/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java
@@ -237,8 +237,7 @@ public final class Cp2RemotePhoneLookup implements PhoneLookup<Cp2Info> {
}
@Override
- public void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ public void registerContentObservers(Context appContext) {
// No content observer needed for remote contacts
}
}
diff --git a/java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java b/java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java
index 9f0b5cf52..7661a15da 100644
--- a/java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java
@@ -152,8 +152,8 @@ public final class SpamPhoneLookup implements PhoneLookup<SpamInfo> {
}
@Override
- public void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
- // No content observer needed for spam info
+ public void registerContentObservers(Context appContext) {
+ // No content observer can be registered as Spam is not based on a content provider.
+ // Each Spam implementation should be responsible for notifying any data changes.
}
}
diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java b/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java
index 8b6fcbc07..8d724afe3 100644
--- a/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java
+++ b/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java
@@ -24,18 +24,16 @@ import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
+import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.dialer.calllog.CallLogComponent;
-import com.android.dialer.calllog.CallLogFramework;
-import com.android.dialer.calllog.CallLogFramework.CallLogUi;
-import com.android.dialer.calllog.RefreshAnnotatedCallLogWorker;
+import com.android.dialer.calllog.RefreshAnnotatedCallLogReceiver;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.DialerExecutorComponent;
-import com.android.dialer.common.concurrent.ThreadUtil;
import com.android.dialer.common.concurrent.UiListener;
import com.android.dialer.glidephotomanager.GlidePhotoManagerComponent;
import com.android.dialer.voicemail.listui.error.VoicemailStatus;
@@ -48,24 +46,11 @@ import java.util.List;
// TODO(uabdullah): Register content observer for VoicemailContract.Status.CONTENT_URI in onStart
/** Fragment for Dialer Voicemail Tab. */
-public final class NewVoicemailFragment extends Fragment
- implements LoaderCallbacks<Cursor>, CallLogUi {
-
- /*
- * This is a reasonable time that it might take between related call log writes, that also
- * shouldn't slow down single-writes too much. For example, when populating the database using
- * the simulator, using this value results in ~6 refresh cycles (on a release build) to write 120
- * call log entries.
- */
- private static final long WAIT_MILLIS = 100L;
-
- private RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker;
- private UiListener<Void> refreshAnnotatedCallLogListener;
- @Nullable private Runnable refreshAnnotatedCallLogRunnable;
-
- private UiListener<ImmutableList<VoicemailStatus>> queryVoicemailStatusTableListener;
+public final class NewVoicemailFragment extends Fragment implements LoaderCallbacks<Cursor> {
private RecyclerView recyclerView;
+ private RefreshAnnotatedCallLogReceiver refreshAnnotatedCallLogReceiver;
+ private UiListener<ImmutableList<VoicemailStatus>> queryVoicemailStatusTableListener;
public NewVoicemailFragment() {
LogUtil.enterBlock("NewVoicemailFragment.NewVoicemailFragment");
@@ -77,23 +62,12 @@ public final class NewVoicemailFragment extends Fragment
LogUtil.enterBlock("NewVoicemailFragment.onActivityCreated");
- CallLogComponent component = CallLogComponent.get(getContext());
- CallLogFramework callLogFramework = component.callLogFramework();
- callLogFramework.attachUi(this);
-
- // TODO(zachh): Use support fragment manager and add support for them in executors library.
- refreshAnnotatedCallLogListener =
- DialerExecutorComponent.get(getContext())
- .createUiListener(
- getActivity().getFragmentManager(), "NewVoicemailFragment.refreshAnnotatedCallLog");
-
+ refreshAnnotatedCallLogReceiver = new RefreshAnnotatedCallLogReceiver(getContext());
queryVoicemailStatusTableListener =
DialerExecutorComponent.get(getContext())
.createUiListener(
getActivity().getFragmentManager(),
"NewVoicemailFragment.queryVoicemailStatusTable");
-
- refreshAnnotatedCallLogWorker = component.getRefreshAnnotatedCallLogWorker();
}
@Override
@@ -108,11 +82,12 @@ public final class NewVoicemailFragment extends Fragment
LogUtil.enterBlock("NewCallLogFragment.onResume");
- CallLogFramework callLogFramework = CallLogComponent.get(getContext()).callLogFramework();
- callLogFramework.attachUi(this);
+ registerRefreshAnnotatedCallLogReceiver();
// TODO(zachh): Consider doing this when fragment becomes visible.
- refreshAnnotatedCallLog(true /* checkDirty */);
+ CallLogComponent.get(getContext())
+ .getRefreshAnnotatedCallLogNotifier()
+ .notify(/* checkDirty = */ true);
}
@Override
@@ -121,14 +96,9 @@ public final class NewVoicemailFragment extends Fragment
LogUtil.enterBlock("NewVoicemailFragment.onPause");
- // This is pending work that we don't actually need to follow through with.
- ThreadUtil.getUiThreadHandler().removeCallbacks(refreshAnnotatedCallLogRunnable);
-
- CallLogFramework callLogFramework = CallLogComponent.get(getContext()).callLogFramework();
- callLogFramework.detachUi();
+ unregisterRefreshAnnotatedCallLogReceiver();
}
- @Nullable
@Override
public View onCreateView(
LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@@ -139,43 +109,6 @@ public final class NewVoicemailFragment extends Fragment
return view;
}
- private void refreshAnnotatedCallLog(boolean checkDirty) {
- LogUtil.enterBlock("NewVoicemailFragment.refreshAnnotatedCallLog");
-
- // If we already scheduled a refresh, cancel it and schedule a new one so that repeated requests
- // in quick succession don't result in too much work. For example, if we get 10 requests in
- // 10ms, and a complete refresh takes a constant 200ms, the refresh will take 300ms (100ms wait
- // and 1 iteration @200ms) instead of 2 seconds (10 iterations @ 200ms) since the work requests
- // are serialized in RefreshAnnotatedCallLogWorker.
- //
- // We might get many requests in quick succession, for example, when the simulator inserts
- // hundreds of rows into the system call log, or when the data for a new call is incrementally
- // written to different columns as it becomes available.
- ThreadUtil.getUiThreadHandler().removeCallbacks(refreshAnnotatedCallLogRunnable);
-
- refreshAnnotatedCallLogRunnable =
- () -> {
- ListenableFuture<Void> future =
- checkDirty
- ? refreshAnnotatedCallLogWorker.refreshWithDirtyCheck()
- : refreshAnnotatedCallLogWorker.refreshWithoutDirtyCheck();
- refreshAnnotatedCallLogListener.listen(
- getContext(),
- future,
- unused -> {},
- throwable -> {
- throw new RuntimeException(throwable);
- });
- };
- ThreadUtil.getUiThreadHandler().postDelayed(refreshAnnotatedCallLogRunnable, WAIT_MILLIS);
- }
-
- @Override
- public void invalidateUi() {
- LogUtil.enterBlock("NewVoicemailFragment.invalidateUi");
- refreshAnnotatedCallLog(false /* checkDirty */);
- }
-
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
LogUtil.enterBlock("NewVoicemailFragment.onCreateLoader");
@@ -210,6 +143,24 @@ public final class NewVoicemailFragment extends Fragment
queryAndUpdateVoicemailStatusAlert();
}
+ private void registerRefreshAnnotatedCallLogReceiver() {
+ LogUtil.enterBlock("NewVoicemailFragment.registerRefreshAnnotatedCallLogReceiver");
+
+ LocalBroadcastManager.getInstance(getContext())
+ .registerReceiver(
+ refreshAnnotatedCallLogReceiver, RefreshAnnotatedCallLogReceiver.getIntentFilter());
+ }
+
+ private void unregisterRefreshAnnotatedCallLogReceiver() {
+ LogUtil.enterBlock("NewVoicemailFragment.unregisterRefreshAnnotatedCallLogReceiver");
+
+ // Cancel pending work as we don't need it any more.
+ CallLogComponent.get(getContext()).getRefreshAnnotatedCallLogNotifier().cancel();
+
+ LocalBroadcastManager.getInstance(getContext())
+ .unregisterReceiver(refreshAnnotatedCallLogReceiver);
+ }
+
private void queryAndUpdateVoicemailStatusAlert() {
queryVoicemailStatusTableListener.listen(
getContext(),