summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/calllog/ui
diff options
context:
space:
mode:
authorlinyuh <linyuh@google.com>2018-05-11 15:58:37 -0700
committerCopybara-Service <copybara-piper@google.com>2018-05-11 17:00:46 -0700
commit34daf89e6a8db5b329424db85a79362536ee8245 (patch)
tree7b7a4e5b130b2dad6f9b40f428c4aa86311517c5 /java/com/android/dialer/calllog/ui
parentc10636d87e9db5cb7c09a6e1c10eae4d7cade901 (diff)
Move coalescing logic out of AnnotatedCallLogContentProvider.
Bug: 79232964 Test: CoalescerTest, AnnotatedCallLogCursorLoaderTest, and manual testing. PiperOrigin-RevId: 196321995 Change-Id: I016bf28e0c09cf4fee5bc5a9115335fb35b7f7e9
Diffstat (limited to 'java/com/android/dialer/calllog/ui')
-rw-r--r--java/com/android/dialer/calllog/ui/AnnotatedCallLogCursorLoader.java36
-rw-r--r--java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java132
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogAdapter.java3
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogFragment.java57
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java8
5 files changed, 88 insertions, 148 deletions
diff --git a/java/com/android/dialer/calllog/ui/AnnotatedCallLogCursorLoader.java b/java/com/android/dialer/calllog/ui/AnnotatedCallLogCursorLoader.java
new file mode 100644
index 000000000..6111cd8bf
--- /dev/null
+++ b/java/com/android/dialer/calllog/ui/AnnotatedCallLogCursorLoader.java
@@ -0,0 +1,36 @@
+/*
+ * 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.ui;
+
+import android.content.Context;
+import android.provider.CallLog.Calls;
+import android.support.v4.content.CursorLoader;
+import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog;
+
+/** Cursor loader for {@link AnnotatedCallLog}. */
+final class AnnotatedCallLogCursorLoader extends CursorLoader {
+
+ AnnotatedCallLogCursorLoader(Context context) {
+ super(
+ context,
+ AnnotatedCallLog.CONTENT_URI,
+ /* projection = */ null,
+ /* selection = */ AnnotatedCallLog.CALL_TYPE + " != ?",
+ /* selectionArgs = */ new String[] {Integer.toString(Calls.VOICEMAIL_TYPE)},
+ /* sortOrder = */ AnnotatedCallLog.TIMESTAMP + " DESC");
+ }
+}
diff --git a/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java b/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java
deleted file mode 100644
index 164bb7dad..000000000
--- a/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2017 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.ui;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.support.v4.content.CursorLoader;
-import android.text.TextUtils;
-import com.android.dialer.CoalescedIds;
-import com.android.dialer.DialerPhoneNumber;
-import com.android.dialer.NumberAttributes;
-import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.CoalescedAnnotatedCallLog;
-import com.android.dialer.calllog.model.CoalescedRow;
-import com.google.protobuf.InvalidProtocolBufferException;
-
-/** CursorLoader for the coalesced annotated call log. */
-final class CoalescedAnnotatedCallLogCursorLoader extends CursorLoader {
-
- // Indexes for CoalescedAnnotatedCallLog.ALL_COLUMNS
- private static final int ID = 0;
- private static final int TIMESTAMP = 1;
- private static final int NUMBER = 2;
- private static final int FORMATTED_NUMBER = 3;
- private static final int NUMBER_PRESENTATION = 4;
- private static final int IS_READ = 5;
- private static final int NEW = 6;
- private static final int GEOCODED_LOCATION = 7;
- private static final int PHONE_ACCOUNT_COMPONENT_NAME = 8;
- private static final int PHONE_ACCOUNT_ID = 9;
- private static final int FEATURES = 10;
- private static final int NUMBER_ATTRIBUTES = 11;
- private static final int IS_VOICEMAIL_CALL = 12;
- private static final int VOICEMAIL_CALL_TAG = 13;
- private static final int CALL_TYPE = 14;
- private static final int COALESCED_IDS = 15;
-
- CoalescedAnnotatedCallLogCursorLoader(Context context) {
- // CoalescedAnnotatedCallLog requires that PROJECTION be ALL_COLUMNS and the following params be
- // null.
- super(
- context,
- CoalescedAnnotatedCallLog.CONTENT_URI,
- CoalescedAnnotatedCallLog.ALL_COLUMNS,
- null,
- null,
- null);
- }
-
- /** Creates a new {@link CoalescedRow} from the provided cursor using the current position. */
- static CoalescedRow toRow(Cursor cursor) {
- DialerPhoneNumber number;
- try {
- number = DialerPhoneNumber.parseFrom(cursor.getBlob(NUMBER));
- } catch (InvalidProtocolBufferException e) {
- throw new IllegalStateException("Couldn't parse DialerPhoneNumber bytes");
- }
-
- CoalescedIds coalescedIds;
- try {
- coalescedIds = CoalescedIds.parseFrom(cursor.getBlob(COALESCED_IDS));
- } catch (InvalidProtocolBufferException e) {
- throw new IllegalStateException("Couldn't parse CoalescedIds bytes");
- }
-
- NumberAttributes numberAttributes;
- try {
- numberAttributes = NumberAttributes.parseFrom(cursor.getBlob(NUMBER_ATTRIBUTES));
- } catch (InvalidProtocolBufferException e) {
- throw new IllegalStateException("Couldn't parse NumberAttributes bytes");
- }
-
- CoalescedRow.Builder coalescedRowBuilder =
- CoalescedRow.newBuilder()
- .setId(cursor.getLong(ID))
- .setTimestamp(cursor.getLong(TIMESTAMP))
- .setNumber(number)
- .setNumberPresentation(cursor.getInt(NUMBER_PRESENTATION))
- .setIsRead(cursor.getInt(IS_READ) == 1)
- .setIsNew(cursor.getInt(NEW) == 1)
- .setFeatures(cursor.getInt(FEATURES))
- .setCallType(cursor.getInt(CALL_TYPE))
- .setNumberAttributes(numberAttributes)
- .setIsVoicemailCall(cursor.getInt(IS_VOICEMAIL_CALL) == 1)
- .setCoalescedIds(coalescedIds);
-
- String formattedNumber = cursor.getString(FORMATTED_NUMBER);
- if (!TextUtils.isEmpty(formattedNumber)) {
- coalescedRowBuilder.setFormattedNumber(formattedNumber);
- }
-
- String geocodedLocation = cursor.getString(GEOCODED_LOCATION);
- if (!TextUtils.isEmpty(geocodedLocation)) {
- coalescedRowBuilder.setGeocodedLocation(geocodedLocation);
- }
-
- String phoneAccountComponentName = cursor.getString(PHONE_ACCOUNT_COMPONENT_NAME);
- if (!TextUtils.isEmpty(phoneAccountComponentName)) {
- coalescedRowBuilder.setPhoneAccountComponentName(
- cursor.getString(PHONE_ACCOUNT_COMPONENT_NAME));
- }
-
- String phoneAccountId = cursor.getString(PHONE_ACCOUNT_ID);
- if (!TextUtils.isEmpty(phoneAccountId)) {
- coalescedRowBuilder.setPhoneAccountId(phoneAccountId);
- }
-
- String voicemailCallTag = cursor.getString(VOICEMAIL_CALL_TAG);
- if (!TextUtils.isEmpty(voicemailCallTag)) {
- coalescedRowBuilder.setVoicemailCallTag(voicemailCallTag);
- }
-
- return coalescedRowBuilder.build();
- }
-
- static long getTimestamp(Cursor cursor) {
- return cursor.getLong(TIMESTAMP);
- }
-}
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java b/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java
index 69cc02be4..501cf1657 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java
@@ -26,6 +26,7 @@ import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.view.LayoutInflater;
import android.view.ViewGroup;
+import com.android.dialer.calllog.database.Coalescer;
import com.android.dialer.calllogutils.CallLogDates;
import com.android.dialer.common.Assert;
import com.android.dialer.duo.Duo;
@@ -147,7 +148,7 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> {
int numItemsInToday = 0;
int numItemsInYesterday = 0;
do {
- long timestamp = CoalescedAnnotatedCallLogCursorLoader.getTimestamp(cursor);
+ long timestamp = Coalescer.getTimestamp(cursor);
long dayDifference = CallLogDates.getDayDifference(currentTimeMillis, timestamp);
if (dayDifference == 0) {
numItemsInToday++;
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
index bc5750770..1890b7433 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
@@ -31,14 +31,18 @@ import android.view.View;
import android.view.ViewGroup;
import com.android.dialer.calllog.CallLogComponent;
import com.android.dialer.calllog.RefreshAnnotatedCallLogReceiver;
+import com.android.dialer.calllog.database.CallLogDatabaseComponent;
import com.android.dialer.common.Assert;
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.SupportUiListener;
import com.android.dialer.common.concurrent.ThreadUtil;
import com.android.dialer.metrics.Metrics;
import com.android.dialer.metrics.MetricsComponent;
import com.android.dialer.metrics.jank.RecyclerViewJankLogger;
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;
@@ -48,8 +52,9 @@ public final class NewCallLogFragment extends Fragment implements LoaderCallback
@VisibleForTesting
static final long MARK_ALL_CALLS_READ_WAIT_MILLIS = TimeUnit.SECONDS.toMillis(3);
- private RefreshAnnotatedCallLogReceiver refreshAnnotatedCallLogReceiver;
private RecyclerView recyclerView;
+ private RefreshAnnotatedCallLogReceiver refreshAnnotatedCallLogReceiver;
+ private SupportUiListener<Cursor> coalesingAnnotatedCallLogListener;
private boolean shouldMarkCallsRead = false;
private final Runnable setShouldMarkCallsReadTrue = () -> shouldMarkCallsRead = true;
@@ -188,6 +193,11 @@ public final class NewCallLogFragment extends Fragment implements LoaderCallback
new RecyclerViewJankLogger(
MetricsComponent.get(getContext()).metrics(), Metrics.NEW_CALL_LOG_JANK_EVENT_NAME));
+ coalesingAnnotatedCallLogListener =
+ DialerExecutorComponent.get(getContext())
+ .createUiListener(
+ getChildFragmentManager(),
+ /* taskId = */ "NewCallLogFragment.coalescingAnnotatedCallLog");
getLoaderManager().restartLoader(0, null, this);
return view;
@@ -214,7 +224,7 @@ public final class NewCallLogFragment extends Fragment implements LoaderCallback
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
LogUtil.enterBlock("NewCallLogFragment.onCreateLoader");
- return new CoalescedAnnotatedCallLogCursorLoader(getContext());
+ return new AnnotatedCallLogCursorLoader(Assert.isNotNull(getContext()));
}
@Override
@@ -228,17 +238,38 @@ public final class NewCallLogFragment extends Fragment implements LoaderCallback
return;
}
- // TODO(zachh): Handle empty cursor by showing empty view.
- if (recyclerView.getAdapter() == null) {
- recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
- // Note: It's not clear if this callback can be invoked when there's no associated activity,
- // but if crashes are observed here it may be possible to use getContext() instead.
- Activity activity = Assert.isNotNull(getActivity());
- recyclerView.setAdapter(
- new NewCallLogAdapter(activity, newCursor, System::currentTimeMillis));
- } else {
- ((NewCallLogAdapter) recyclerView.getAdapter()).updateCursor(newCursor);
- }
+ // Start combining adjacent rows which should be collapsed for display purposes.
+ // This is a time-consuming process so we will do it in the background.
+ ListenableFuture<Cursor> coalescedCursorFuture =
+ CallLogDatabaseComponent.get(getContext()).coalescer().coalesce(newCursor);
+
+ coalesingAnnotatedCallLogListener.listen(
+ getContext(),
+ coalescedCursorFuture,
+ coalescedCursor -> {
+ LogUtil.i("NewCallLogFragment.onLoadFinished", "coalescing succeeded");
+
+ // TODO(zachh): Handle empty cursor by showing empty view.
+ if (recyclerView.getAdapter() == null) {
+ recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+ // Note: It's not clear if this callback can be invoked when there's no associated
+ // activity, but if crashes are observed here it may be possible to use getContext()
+ // instead.
+ Activity activity = Assert.isNotNull(getActivity());
+ recyclerView.setAdapter(
+ new NewCallLogAdapter(activity, coalescedCursor, System::currentTimeMillis));
+ } else {
+ ((NewCallLogAdapter) recyclerView.getAdapter()).updateCursor(coalescedCursor);
+ }
+ },
+ throwable -> {
+ // Coalescing can fail if the cursor passed to Coalescer is closed by the loader while
+ // the work is still in progress.
+ // This can happen when the loader restarts and finishes loading data before the
+ // coalescing work is completed.
+ // TODO(linyuh): throw an exception here if the failure above can be avoided.
+ LogUtil.e("NewCallLogFragment.onLoadFinished", "coalescing failed", throwable);
+ });
}
@Override
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java b/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
index 5f3cd96c4..fccd8b9c4 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
@@ -29,6 +29,7 @@ import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.dialer.calllog.database.Coalescer;
import com.android.dialer.calllog.model.CoalescedRow;
import com.android.dialer.calllog.ui.NewCallLogAdapter.PopCounts;
import com.android.dialer.calllog.ui.menu.NewCallLogMenu;
@@ -96,9 +97,12 @@ final class NewCallLogViewHolder extends RecyclerView.ViewHolder {
uiExecutorService = DialerExecutorComponent.get(activity).uiExecutor();
}
- /** @param cursor a cursor from {@link CoalescedAnnotatedCallLogCursorLoader}. */
+ /**
+ * @param cursor a cursor for {@link
+ * com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.CoalescedAnnotatedCallLog}.
+ */
void bind(Cursor cursor) {
- CoalescedRow row = CoalescedAnnotatedCallLogCursorLoader.toRow(cursor);
+ CoalescedRow row = Coalescer.toRow(cursor);
currentRowId = row.getId(); // Used to make sure async updates are applied to the correct views
// Even if there is additional real time processing necessary, we still want to immediately show