summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/calllog
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/dialer/calllog')
-rw-r--r--java/com/android/dialer/calllog/CallLogComponent.java2
-rw-r--r--java/com/android/dialer/calllog/CallLogFramework.java3
-rw-r--r--java/com/android/dialer/calllog/CallLogModule.java8
-rw-r--r--java/com/android/dialer/calllog/DataSources.java (renamed from java/com/android/dialer/calllog/datasources/DataSources.java)5
-rw-r--r--java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java113
-rw-r--r--java/com/android/dialer/calllog/database/AndroidManifest.xml28
-rw-r--r--java/com/android/dialer/calllog/database/AnnotatedCallLog.java53
-rw-r--r--java/com/android/dialer/calllog/database/AnnotatedCallLogContentProvider.java310
-rw-r--r--java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java17
-rw-r--r--java/com/android/dialer/calllog/database/CallLogDatabaseComponent.java40
-rw-r--r--java/com/android/dialer/calllog/database/CallLogMutations.java58
-rw-r--r--java/com/android/dialer/calllog/database/Coalescer.java142
-rw-r--r--java/com/android/dialer/calllog/database/MutationApplier.java105
-rw-r--r--java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java114
-rw-r--r--java/com/android/dialer/calllog/datasources/CallLogDataSource.java67
-rw-r--r--java/com/android/dialer/calllog/datasources/CallLogMutations.java110
-rw-r--r--java/com/android/dialer/calllog/datasources/contacts/ContactsDataSource.java22
-rw-r--r--java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java209
-rw-r--r--java/com/android/dialer/calllog/datasources/util/RowCombiner.java53
-rw-r--r--java/com/android/dialer/calllog/testing/FakeCallLogApplication.java39
-rw-r--r--java/com/android/dialer/calllog/ui/AndroidManifest.xml2
-rw-r--r--java/com/android/dialer/calllog/ui/AnnotatedCallLogCursorLoader.java48
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogAdapter.java53
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogFragment.java93
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java43
-rw-r--r--java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml16
-rw-r--r--java/com/android/dialer/calllog/ui/res/layout/new_call_log_fragment.xml7
27 files changed, 315 insertions, 1445 deletions
diff --git a/java/com/android/dialer/calllog/CallLogComponent.java b/java/com/android/dialer/calllog/CallLogComponent.java
index c7db2a1b8..5cdd2b4d0 100644
--- a/java/com/android/dialer/calllog/CallLogComponent.java
+++ b/java/com/android/dialer/calllog/CallLogComponent.java
@@ -25,8 +25,6 @@ public abstract class CallLogComponent {
public abstract CallLogFramework callLogFramework();
- public abstract RefreshAnnotatedCallLogWorker getRefreshAnnotatedCallLogWorker();
-
public static CallLogComponent get(Context context) {
return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component())
.callLogComponent();
diff --git a/java/com/android/dialer/calllog/CallLogFramework.java b/java/com/android/dialer/calllog/CallLogFramework.java
index 55ef15b47..508413b14 100644
--- a/java/com/android/dialer/calllog/CallLogFramework.java
+++ b/java/com/android/dialer/calllog/CallLogFramework.java
@@ -22,7 +22,6 @@ import android.preference.PreferenceManager;
import android.support.annotation.MainThread;
import android.support.annotation.Nullable;
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.ConfigProviderBindings;
import com.android.dialer.common.LogUtil;
@@ -39,6 +38,7 @@ import javax.inject.Singleton;
public final class CallLogFramework implements CallLogDataSource.ContentObserverCallbacks {
static final String PREF_FORCE_REBUILD = "callLogFrameworkForceRebuild";
+ static final String PREF_LAST_REBUILD_TIMESTAMP_MILLIS = "callLogFrameworkLastRebuild";
private final DataSources dataSources;
@@ -58,7 +58,6 @@ public final class CallLogFramework implements CallLogDataSource.ContentObserver
LogUtil.enterBlock("CallLogFramework.registerContentObservers");
if (!isNewCallLogEnabled(appContext)) {
- LogUtil.i("CallLogFramework.registerContentObservers", "new call log not enabled");
return;
}
diff --git a/java/com/android/dialer/calllog/CallLogModule.java b/java/com/android/dialer/calllog/CallLogModule.java
index 2f2f16d5b..d7473a75e 100644
--- a/java/com/android/dialer/calllog/CallLogModule.java
+++ b/java/com/android/dialer/calllog/CallLogModule.java
@@ -17,9 +17,11 @@
package com.android.dialer.calllog;
import com.android.dialer.calllog.datasources.CallLogDataSource;
-import com.android.dialer.calllog.datasources.DataSources;
import com.android.dialer.calllog.datasources.contacts.ContactsDataSource;
import com.android.dialer.calllog.datasources.systemcalllog.SystemCallLogDataSource;
+import com.android.dialer.common.concurrent.DefaultDialerExecutorFactory;
+import com.android.dialer.common.concurrent.DialerExecutorFactory;
+import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import java.util.Arrays;
@@ -30,6 +32,10 @@ import java.util.List;
@Module
public abstract class CallLogModule {
+ @Binds
+ abstract DialerExecutorFactory bindDialerExecutorFactory(
+ DefaultDialerExecutorFactory defaultDialerExecutorFactory);
+
@Provides
static DataSources provideCallLogDataSources(
SystemCallLogDataSource systemCallLogDataSource, ContactsDataSource contactsDataSource) {
diff --git a/java/com/android/dialer/calllog/datasources/DataSources.java b/java/com/android/dialer/calllog/DataSources.java
index 911ca3fa3..21d190167 100644
--- a/java/com/android/dialer/calllog/datasources/DataSources.java
+++ b/java/com/android/dialer/calllog/DataSources.java
@@ -14,13 +14,14 @@
* limitations under the License
*/
-package com.android.dialer.calllog.datasources;
+package com.android.dialer.calllog;
+import com.android.dialer.calllog.datasources.CallLogDataSource;
import com.android.dialer.calllog.datasources.systemcalllog.SystemCallLogDataSource;
import java.util.List;
/** Immutable lists of data sources used to populate the annotated call log. */
-public interface DataSources {
+interface DataSources {
SystemCallLogDataSource getSystemCallLogDataSource();
diff --git a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
index d25ec5e65..f9f0c9935 100644
--- a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
+++ b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
@@ -18,16 +18,14 @@ package com.android.dialer.calllog;
import android.annotation.TargetApi;
import android.content.Context;
-import android.content.OperationApplicationException;
import android.content.SharedPreferences;
+import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
-import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.support.annotation.WorkerThread;
-import com.android.dialer.calllog.database.CallLogDatabaseComponent;
+import com.android.dialer.calllog.database.AnnotatedCallLog;
+import com.android.dialer.calllog.database.CallLogMutations;
import com.android.dialer.calllog.datasources.CallLogDataSource;
-import com.android.dialer.calllog.datasources.CallLogMutations;
-import com.android.dialer.calllog.datasources.DataSources;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.DialerExecutor.Worker;
@@ -36,65 +34,75 @@ import javax.inject.Inject;
/**
* Worker which brings the annotated call log up to date, if necessary.
*
- * <p>Accepts a boolean which indicates if the dirty check should be skipped.
+ * <p>Accepts a boolean which indicates if the dirty check should be skipped, and returns true if
+ * the annotated call log was updated.
*/
-public class RefreshAnnotatedCallLogWorker implements Worker<Boolean, Void> {
+public class RefreshAnnotatedCallLogWorker implements Worker<Boolean, Boolean> {
private final Context appContext;
private final DataSources dataSources;
@Inject
- RefreshAnnotatedCallLogWorker(Context appContext, DataSources dataSources) {
+ public RefreshAnnotatedCallLogWorker(Context appContext, DataSources dataSources) {
this.appContext = appContext;
this.dataSources = dataSources;
}
@Override
- public Void doInBackground(Boolean skipDirtyCheck)
- throws RemoteException, OperationApplicationException {
- LogUtil.enterBlock("RefreshAnnotatedCallLogWorker.doInBackground");
+ public Boolean doInBackground(Boolean skipDirtyCheck) {
+ LogUtil.enterBlock("RefreshAnnotatedCallLogWorker.doInBackgroundFallible");
long startTime = System.currentTimeMillis();
- checkDirtyAndRebuildIfNecessary(appContext, skipDirtyCheck);
+ boolean annotatedCallLogUpdated = checkDirtyAndRebuildIfNecessary(appContext, skipDirtyCheck);
LogUtil.i(
- "RefreshAnnotatedCallLogWorker.doInBackground",
- "took %dms",
+ "RefreshAnnotatedCallLogWorker.doInBackgroundFallible",
+ "updated? %s, took %dms",
+ annotatedCallLogUpdated,
System.currentTimeMillis() - startTime);
- return null;
+ return annotatedCallLogUpdated;
}
@WorkerThread
- private void checkDirtyAndRebuildIfNecessary(Context appContext, boolean skipDirtyCheck)
- throws RemoteException, OperationApplicationException {
+ private boolean checkDirtyAndRebuildIfNecessary(Context appContext, boolean skipDirtyCheck) {
Assert.isWorkerThread();
long startTime = System.currentTimeMillis();
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext);
- // 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.
+ long lastRebuildTimeMillis =
+ sharedPreferences.getLong(CallLogFramework.PREF_LAST_REBUILD_TIMESTAMP_MILLIS, 0);
+ if (lastRebuildTimeMillis == 0) {
+ LogUtil.i(
+ "RefreshAnnotatedCallLogWorker.checkDirtyAndRebuildIfNecessary",
+ "annotated call log has never been built, marking it dirty");
+ }
boolean forceRebuildPrefValue =
- sharedPreferences.getBoolean(CallLogFramework.PREF_FORCE_REBUILD, true);
+ sharedPreferences.getBoolean(CallLogFramework.PREF_FORCE_REBUILD, false);
if (forceRebuildPrefValue) {
LogUtil.i(
"RefreshAnnotatedCallLogWorker.checkDirtyAndRebuildIfNecessary",
- "annotated call log has been marked dirty or does not exist");
+ "call log has been marked dirty");
}
- boolean isDirty = skipDirtyCheck || forceRebuildPrefValue || isDirty(appContext);
-
+ boolean isDirty =
+ lastRebuildTimeMillis == 0
+ || skipDirtyCheck
+ || forceRebuildPrefValue
+ || isDirty(appContext);
LogUtil.i(
"RefreshAnnotatedCallLogWorker.checkDirtyAndRebuildIfNecessary",
"isDirty took: %dms",
System.currentTimeMillis() - startTime);
if (isDirty) {
startTime = System.currentTimeMillis();
- rebuild(appContext);
+ rebuild(appContext, lastRebuildTimeMillis);
LogUtil.i(
"RefreshAnnotatedCallLogWorker.checkDirtyAndRebuildIfNecessary",
"rebuild took: %dms",
System.currentTimeMillis() - startTime);
+ return true; // Annotated call log was updated.
}
+ return false; // Annotated call log was not updated.
}
@WorkerThread
@@ -121,48 +129,51 @@ public class RefreshAnnotatedCallLogWorker implements Worker<Boolean, Void> {
@TargetApi(Build.VERSION_CODES.M) // Uses try-with-resources
@WorkerThread
- private void rebuild(Context appContext) throws RemoteException, OperationApplicationException {
+ private void rebuild(Context appContext, long lastRebuildTimeMillis) {
Assert.isWorkerThread();
- CallLogMutations mutations = new CallLogMutations();
+ // TODO: Start a transaction?
+ try (SQLiteDatabase database = AnnotatedCallLog.getWritableDatabase(appContext)) {
- // System call log data source must go first!
- CallLogDataSource systemCallLogDataSource = dataSources.getSystemCallLogDataSource();
- String dataSourceName = getName(systemCallLogDataSource);
- LogUtil.i("RefreshAnnotatedCallLogWorker.rebuild", "filling %s", dataSourceName);
- long startTime = System.currentTimeMillis();
- systemCallLogDataSource.fill(appContext, mutations);
- LogUtil.i(
- "RefreshAnnotatedCallLogWorker.rebuild",
- "%s.fill took: %dms",
- dataSourceName,
- System.currentTimeMillis() - startTime);
+ CallLogMutations mutations = new CallLogMutations();
- for (CallLogDataSource dataSource : dataSources.getDataSourcesExcludingSystemCallLog()) {
- dataSourceName = getName(dataSource);
+ // System call log data source must go first!
+ CallLogDataSource systemCallLogDataSource = dataSources.getSystemCallLogDataSource();
+ String dataSourceName = getName(systemCallLogDataSource);
LogUtil.i("RefreshAnnotatedCallLogWorker.rebuild", "filling %s", dataSourceName);
- startTime = System.currentTimeMillis();
- dataSource.fill(appContext, mutations);
+ long startTime = System.currentTimeMillis();
+ systemCallLogDataSource.fill(appContext, database, lastRebuildTimeMillis, mutations);
LogUtil.i(
- "CallLogFramework.rebuild",
+ "RefreshAnnotatedCallLogWorker.rebuild",
"%s.fill took: %dms",
dataSourceName,
System.currentTimeMillis() - startTime);
+
+ for (CallLogDataSource dataSource : dataSources.getDataSourcesExcludingSystemCallLog()) {
+ dataSourceName = getName(dataSource);
+ LogUtil.i("RefreshAnnotatedCallLogWorker.rebuild", "filling %s", dataSourceName);
+ startTime = System.currentTimeMillis();
+ dataSource.fill(appContext, database, lastRebuildTimeMillis, mutations);
+ LogUtil.i(
+ "CallLogFramework.rebuild",
+ "%s.fill took: %dms",
+ dataSourceName,
+ System.currentTimeMillis() - startTime);
+ }
+ LogUtil.i("RefreshAnnotatedCallLogWorker.rebuild", "applying mutations to database");
+ startTime = System.currentTimeMillis();
+ mutations.applyToDatabase(database);
+ LogUtil.i(
+ "RefreshAnnotatedCallLogWorker.rebuild",
+ "applyToDatabase took: %dms",
+ System.currentTimeMillis() - startTime);
}
- LogUtil.i("RefreshAnnotatedCallLogWorker.rebuild", "applying mutations to database");
- startTime = System.currentTimeMillis();
- CallLogDatabaseComponent.get(appContext)
- .mutationApplier()
- .applyToDatabase(mutations, appContext);
- LogUtil.i(
- "RefreshAnnotatedCallLogWorker.rebuild",
- "applyToDatabase took: %dms",
- System.currentTimeMillis() - startTime);
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext);
sharedPreferences
.edit()
.putBoolean(CallLogFramework.PREF_FORCE_REBUILD, false)
+ .putLong(CallLogFramework.PREF_LAST_REBUILD_TIMESTAMP_MILLIS, System.currentTimeMillis())
.commit();
}
diff --git a/java/com/android/dialer/calllog/database/AndroidManifest.xml b/java/com/android/dialer/calllog/database/AndroidManifest.xml
deleted file mode 100644
index 396a6d9a1..000000000
--- a/java/com/android/dialer/calllog/database/AndroidManifest.xml
+++ /dev/null
@@ -1,28 +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
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.dialer.calllog.database">
-
- <application>
-
- <provider
- android:authorities="com.android.dialer.annotatedcalllog"
- android:exported="false"
- android:multiprocess="false"
- android:name=".AnnotatedCallLogContentProvider"/>
-
- </application>
-</manifest>
diff --git a/java/com/android/dialer/calllog/database/AnnotatedCallLog.java b/java/com/android/dialer/calllog/database/AnnotatedCallLog.java
new file mode 100644
index 000000000..7dca44a60
--- /dev/null
+++ b/java/com/android/dialer/calllog/database/AnnotatedCallLog.java
@@ -0,0 +1,53 @@
+/*
+ * 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.database;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.support.annotation.WorkerThread;
+import com.android.dialer.common.Assert;
+
+/** Static methods and constants for interacting with the annotated call log table. */
+public final class AnnotatedCallLog {
+
+ private static final String DATABASE_NAME = "annotated_call_log.db";
+
+ public static final String TABLE_NAME = "AnnotatedCallLog";
+
+ /** Column names for the annotated call log table. */
+ public static final class Columns {
+ public static final String ID = "_id";
+ public static final String TIMESTAMP = "timestamp";
+ public static final String CONTACT_NAME = "contact_name";
+ }
+
+ private AnnotatedCallLog() {}
+
+ @WorkerThread
+ public static SQLiteDatabase getWritableDatabase(Context appContext) {
+ Assert.isWorkerThread();
+
+ return new AnnotatedCallLogDatabaseHelper(appContext, DATABASE_NAME).getWritableDatabase();
+ }
+
+ @WorkerThread
+ public static SQLiteDatabase getReadableDatabase(Context appContext) {
+ Assert.isWorkerThread();
+
+ return new AnnotatedCallLogDatabaseHelper(appContext, DATABASE_NAME).getReadableDatabase();
+ }
+}
diff --git a/java/com/android/dialer/calllog/database/AnnotatedCallLogContentProvider.java b/java/com/android/dialer/calllog/database/AnnotatedCallLogContentProvider.java
deleted file mode 100644
index a9c0d36b0..000000000
--- a/java/com/android/dialer/calllog/database/AnnotatedCallLogContentProvider.java
+++ /dev/null
@@ -1,310 +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.database;
-
-import android.annotation.TargetApi;
-import android.content.ContentProvider;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.OperationApplicationException;
-import android.content.UriMatcher;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.net.Uri;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract;
-import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog;
-import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.CoalescedAnnotatedCallLog;
-import com.android.dialer.common.Assert;
-import com.android.dialer.common.LogUtil;
-import java.util.ArrayList;
-
-/** {@link ContentProvider} for the annotated call log. */
-public class AnnotatedCallLogContentProvider extends ContentProvider {
-
- private static final int ANNOTATED_CALL_LOG_TABLE_CODE = 1;
- private static final int ANNOTATED_CALL_LOG_TABLE_ID_CODE = 2;
- private static final int COALESCED_ANNOTATED_CALL_LOG_TABLE_CODE = 3;
-
- private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-
- static {
- uriMatcher.addURI(
- AnnotatedCallLogContract.AUTHORITY, AnnotatedCallLog.TABLE, ANNOTATED_CALL_LOG_TABLE_CODE);
- uriMatcher.addURI(
- AnnotatedCallLogContract.AUTHORITY,
- AnnotatedCallLog.TABLE + "/#",
- ANNOTATED_CALL_LOG_TABLE_ID_CODE);
- uriMatcher.addURI(
- AnnotatedCallLogContract.AUTHORITY,
- CoalescedAnnotatedCallLog.TABLE,
- COALESCED_ANNOTATED_CALL_LOG_TABLE_CODE);
- }
-
- private AnnotatedCallLogDatabaseHelper databaseHelper;
- private Coalescer coalescer;
-
- private final ThreadLocal<Boolean> applyingBatch = new ThreadLocal<>();
-
- /** Ensures that only a single notification is generated from {@link #applyBatch(ArrayList)}. */
- private boolean isApplyingBatch() {
- return applyingBatch.get() != null && applyingBatch.get();
- }
-
- @Override
- public boolean onCreate() {
- databaseHelper = new AnnotatedCallLogDatabaseHelper(getContext());
- coalescer = CallLogDatabaseComponent.get(getContext()).coalescer();
- return true;
- }
-
- @TargetApi(Build.VERSION_CODES.M) // Uses try-with-resources
- @Nullable
- @Override
- public Cursor query(
- @NonNull Uri uri,
- @Nullable String[] projection,
- @Nullable String selection,
- @Nullable String[] selectionArgs,
- @Nullable String sortOrder) {
- SQLiteDatabase db = databaseHelper.getReadableDatabase();
- SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
- queryBuilder.setTables(AnnotatedCallLog.TABLE);
- int match = uriMatcher.match(uri);
- switch (match) {
- case ANNOTATED_CALL_LOG_TABLE_ID_CODE:
- queryBuilder.appendWhere(AnnotatedCallLog._ID + "=" + ContentUris.parseId(uri));
- // fall through
- case ANNOTATED_CALL_LOG_TABLE_CODE:
- Cursor cursor =
- queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
- if (cursor != null) {
- cursor.setNotificationUri(
- getContext().getContentResolver(), AnnotatedCallLog.CONTENT_URI);
- } else {
- LogUtil.w("AnnotatedCallLogContentProvider.query", "cursor was null");
- }
- return cursor;
- case COALESCED_ANNOTATED_CALL_LOG_TABLE_CODE:
- Assert.checkArgument(projection == null, "projection not supported for coalesced call log");
- Assert.checkArgument(selection == null, "selection not supported for coalesced call log");
- Assert.checkArgument(
- selectionArgs == null, "selection args not supported for coalesced call log");
- Assert.checkArgument(sortOrder == null, "sort order not supported for coalesced call log");
- try (Cursor allAnnotatedCallLogRows =
- queryBuilder.query(
- db, null, null, null, null, null, AnnotatedCallLog.TIMESTAMP + " DESC")) {
- Cursor coalescedRows = coalescer.coalesce(allAnnotatedCallLogRows);
- coalescedRows.setNotificationUri(
- getContext().getContentResolver(), CoalescedAnnotatedCallLog.CONTENT_URI);
- return coalescedRows;
- }
- default:
- throw new IllegalArgumentException("Unknown uri: " + uri);
- }
- }
-
- @Nullable
- @Override
- public String getType(@NonNull Uri uri) {
- return AnnotatedCallLog.CONTENT_ITEM_TYPE;
- }
-
- @Nullable
- @Override
- public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
- // Javadoc states values is not nullable, even though it is annotated as such (b/38123194)!
- Assert.checkArgument(values != null);
-
- SQLiteDatabase database = databaseHelper.getWritableDatabase();
- int match = uriMatcher.match(uri);
- switch (match) {
- case ANNOTATED_CALL_LOG_TABLE_CODE:
- Assert.checkArgument(
- values.get(AnnotatedCallLog._ID) != null, "You must specify an _ID when inserting");
- break;
- case ANNOTATED_CALL_LOG_TABLE_ID_CODE:
- Long idFromUri = ContentUris.parseId(uri);
- Long idFromValues = values.getAsLong(AnnotatedCallLog._ID);
- Assert.checkArgument(
- idFromValues == null || idFromValues.equals(idFromUri),
- "_ID from values %d does not match ID from URI: %s",
- idFromValues,
- uri);
- if (idFromValues == null) {
- values.put(AnnotatedCallLog._ID, idFromUri);
- }
- break;
- case COALESCED_ANNOTATED_CALL_LOG_TABLE_CODE:
- throw new UnsupportedOperationException("coalesced call log does not support inserting");
- default:
- throw new IllegalArgumentException("Unknown uri: " + uri);
- }
- long id = database.insert(AnnotatedCallLog.TABLE, null, values);
- if (id < 0) {
- LogUtil.w(
- "AnnotatedCallLogContentProvider.insert",
- "error inserting row with id: %d",
- values.get(AnnotatedCallLog._ID));
- return null;
- }
- Uri insertedUri = ContentUris.withAppendedId(AnnotatedCallLog.CONTENT_URI, id);
- if (!isApplyingBatch()) {
- notifyChange(insertedUri);
- }
- return insertedUri;
- }
-
- @Override
- public int delete(
- @NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
- SQLiteDatabase database = databaseHelper.getWritableDatabase();
- final int match = uriMatcher.match(uri);
- switch (match) {
- case ANNOTATED_CALL_LOG_TABLE_CODE:
- break;
- case ANNOTATED_CALL_LOG_TABLE_ID_CODE:
- Assert.checkArgument(selection == null, "Do not specify selection when deleting by ID");
- Assert.checkArgument(
- selectionArgs == null, "Do not specify selection args when deleting by ID");
- long id = ContentUris.parseId(uri);
- Assert.checkArgument(id != -1, "error parsing id from uri %s", uri);
- selection = getSelectionWithId(id);
- break;
- case COALESCED_ANNOTATED_CALL_LOG_TABLE_CODE:
- throw new UnsupportedOperationException("coalesced call log does not support deleting");
- default:
- throw new IllegalArgumentException("Unknown uri: " + uri);
- }
- int rows = database.delete(AnnotatedCallLog.TABLE, selection, selectionArgs);
- if (rows > 0) {
- if (!isApplyingBatch()) {
- notifyChange(uri);
- }
- } else {
- LogUtil.w("AnnotatedCallLogContentProvider.delete", "no rows deleted");
- }
- return rows;
- }
-
- @Override
- public int update(
- @NonNull Uri uri,
- @Nullable ContentValues values,
- @Nullable String selection,
- @Nullable String[] selectionArgs) {
- // Javadoc states values is not nullable, even though it is annotated as such (b/38123194)!
- Assert.checkArgument(values != null);
-
- SQLiteDatabase database = databaseHelper.getWritableDatabase();
- int match = uriMatcher.match(uri);
- switch (match) {
- case ANNOTATED_CALL_LOG_TABLE_CODE:
- break;
- case ANNOTATED_CALL_LOG_TABLE_ID_CODE:
- Assert.checkArgument(
- !values.containsKey(AnnotatedCallLog._ID), "Do not specify _ID when updating by ID");
- Assert.checkArgument(selection == null, "Do not specify selection when updating by ID");
- Assert.checkArgument(
- selectionArgs == null, "Do not specify selection args when updating by ID");
- selection = getSelectionWithId(ContentUris.parseId(uri));
- break;
- case COALESCED_ANNOTATED_CALL_LOG_TABLE_CODE:
- throw new UnsupportedOperationException("coalesced call log does not support updating");
- default:
- throw new IllegalArgumentException("Unknown uri: " + uri);
- }
- int rows = database.update(AnnotatedCallLog.TABLE, values, selection, selectionArgs);
- if (rows > 0) {
- if (!isApplyingBatch()) {
- notifyChange(uri);
- }
- } else {
- LogUtil.w("AnnotatedCallLogContentProvider.update", "no rows updated");
- }
- return rows;
- }
-
- /**
- * {@inheritDoc}
- *
- * <p>Note: When applyBatch is used with the AnnotatedCallLog, only a single notification for the
- * content URI is generated, not individual notifications for each affected URI.
- */
- @NonNull
- @Override
- public ContentProviderResult[] applyBatch(@NonNull ArrayList<ContentProviderOperation> operations)
- throws OperationApplicationException {
- ContentProviderResult[] results = new ContentProviderResult[operations.size()];
- if (operations.isEmpty()) {
- return results;
- }
-
- SQLiteDatabase database = databaseHelper.getWritableDatabase();
- try {
- applyingBatch.set(true);
- database.beginTransaction();
- for (int i = 0; i < operations.size(); i++) {
- ContentProviderOperation operation = operations.get(i);
- int match = uriMatcher.match(operation.getUri());
- switch (match) {
- case ANNOTATED_CALL_LOG_TABLE_CODE:
- case ANNOTATED_CALL_LOG_TABLE_ID_CODE:
- // These are allowed values, continue.
- break;
- case COALESCED_ANNOTATED_CALL_LOG_TABLE_CODE:
- throw new UnsupportedOperationException(
- "coalesced call log does not support applyBatch");
- default:
- throw new IllegalArgumentException("Unknown uri: " + operation.getUri());
- }
- ContentProviderResult result = operation.apply(this, results, i);
- if (operations.get(i).isInsert()) {
- if (result.uri == null) {
- throw new OperationApplicationException("error inserting row");
- }
- } else if (result.count == 0) {
- throw new OperationApplicationException("error updating or deleting rows");
- }
- results[i] = result;
- }
- database.setTransactionSuccessful();
- } finally {
- applyingBatch.set(false);
- database.endTransaction();
- }
- notifyChange(AnnotatedCallLog.CONTENT_URI);
- return results;
- }
-
- private String getSelectionWithId(long id) {
- return AnnotatedCallLog._ID + "=" + id;
- }
-
- private void notifyChange(Uri uri) {
- getContext().getContentResolver().notifyChange(uri, null);
- // Any time the annotated call log changes, we need to also notify observers of the
- // CoalescedAnnotatedCallLog, since that is just a massaged in-memory view of the real annotated
- // call log table.
- getContext().getContentResolver().notifyChange(CoalescedAnnotatedCallLog.CONTENT_URI, null);
- }
-}
diff --git a/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java b/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java
index 3cca639ff..7b28e5505 100644
--- a/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java
+++ b/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java
@@ -16,25 +16,28 @@
package com.android.dialer.calllog.database;
+import static com.android.dialer.calllog.database.AnnotatedCallLog.Columns.CONTACT_NAME;
+import static com.android.dialer.calllog.database.AnnotatedCallLog.Columns.ID;
+import static com.android.dialer.calllog.database.AnnotatedCallLog.Columns.TIMESTAMP;
+
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
-import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog;
import com.android.dialer.common.LogUtil;
/** {@link SQLiteOpenHelper} for the AnnotatedCallLog database. */
class AnnotatedCallLogDatabaseHelper extends SQLiteOpenHelper {
- AnnotatedCallLogDatabaseHelper(Context appContext) {
- super(appContext, "annotated_call_log.db", null, 1);
+ AnnotatedCallLogDatabaseHelper(Context appContext, String databaseName) {
+ super(appContext, databaseName, null, 1);
}
private static final String CREATE_SQL =
new StringBuilder()
- .append("create table if not exists " + AnnotatedCallLog.TABLE + " (")
- .append(AnnotatedCallLog._ID + " integer primary key, ")
- .append(AnnotatedCallLog.TIMESTAMP + " integer, ")
- .append(AnnotatedCallLog.CONTACT_NAME + " string")
+ .append("create table if not exists " + AnnotatedCallLog.TABLE_NAME + " (")
+ .append(ID + " integer primary key, ")
+ .append(TIMESTAMP + " integer, ")
+ .append(CONTACT_NAME + " string")
.append(");")
.toString();
diff --git a/java/com/android/dialer/calllog/database/CallLogDatabaseComponent.java b/java/com/android/dialer/calllog/database/CallLogDatabaseComponent.java
deleted file mode 100644
index ede46911c..000000000
--- a/java/com/android/dialer/calllog/database/CallLogDatabaseComponent.java
+++ /dev/null
@@ -1,40 +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.database;
-
-import android.content.Context;
-import com.android.dialer.inject.HasRootComponent;
-import dagger.Subcomponent;
-
-/** Dagger component for database package. */
-@Subcomponent
-public abstract class CallLogDatabaseComponent {
-
- public abstract Coalescer coalescer();
-
- public abstract MutationApplier mutationApplier();
-
- public static CallLogDatabaseComponent get(Context context) {
- return ((CallLogDatabaseComponent.HasComponent)
- ((HasRootComponent) context.getApplicationContext()).component())
- .callLogDatabaseComponent();
- }
-
- /** Used to refer to the root application component. */
- public interface HasComponent {
- CallLogDatabaseComponent callLogDatabaseComponent();
- }
-}
diff --git a/java/com/android/dialer/calllog/database/CallLogMutations.java b/java/com/android/dialer/calllog/database/CallLogMutations.java
new file mode 100644
index 000000000..ec020c6af
--- /dev/null
+++ b/java/com/android/dialer/calllog/database/CallLogMutations.java
@@ -0,0 +1,58 @@
+/*
+ * 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.database;
+
+import android.content.ContentValues;
+import android.database.sqlite.SQLiteDatabase;
+import android.support.annotation.WorkerThread;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import com.android.dialer.common.Assert;
+
+/** A collection of mutations to the annotated call log. */
+public final class CallLogMutations {
+
+ private final ArrayMap<Integer, ContentValues> inserts = new ArrayMap<>();
+ private final ArrayMap<Integer, ContentValues> updates = new ArrayMap<>();
+ private final ArraySet<Integer> deletes = new ArraySet<>();
+
+ /** @param contentValues an entire row not including the ID */
+ public void insert(int id, ContentValues contentValues) {
+ inserts.put(id, contentValues);
+ }
+
+ /** @param contentValues the specific columns to update, not including the ID. */
+ public void update(int id, ContentValues contentValues) {
+ // TODO: Consider merging automatically.
+ updates.put(id, contentValues);
+ }
+
+ public void delete(int id) {
+ deletes.add(id);
+ }
+
+ public boolean isEmpty() {
+ return inserts.isEmpty() && updates.isEmpty() && deletes.isEmpty();
+ }
+
+ @WorkerThread
+ public void applyToDatabase(SQLiteDatabase writableDatabase) {
+ Assert.isWorkerThread();
+
+ // TODO: Implementation.
+ }
+}
diff --git a/java/com/android/dialer/calllog/database/Coalescer.java b/java/com/android/dialer/calllog/database/Coalescer.java
deleted file mode 100644
index e3dfb7ece..000000000
--- a/java/com/android/dialer/calllog/database/Coalescer.java
+++ /dev/null
@@ -1,142 +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.database;
-
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.MatrixCursor;
-import android.support.annotation.NonNull;
-import android.support.annotation.WorkerThread;
-import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog;
-import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.CoalescedAnnotatedCallLog;
-import com.android.dialer.calllog.datasources.CallLogDataSource;
-import com.android.dialer.calllog.datasources.DataSources;
-import com.android.dialer.common.Assert;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import javax.inject.Inject;
-
-/**
- * Coalesces call log rows by combining some adjacent rows.
- *
- * <p>Applies the business which logic which determines which adjacent rows should be coalasced, and
- * then delegates to each data source to determine how individual columns should be aggregated.
- */
-public class Coalescer {
-
- private final DataSources dataSources;
-
- @Inject
- Coalescer(DataSources dataSources) {
- this.dataSources = dataSources;
- }
-
- /**
- * Reads the entire {@link AnnotatedCallLog} database into memory from the provided {@code
- * allAnnotatedCallLog} parameter and then builds and returns a new {@link MatrixCursor} which is
- * the result of combining adjacent rows which should be collapsed for display purposes.
- *
- * @param allAnnotatedCallLogRowsSortedByTimestampDesc all {@link AnnotatedCallLog} rows, sorted
- * by timestamp descending
- * @return a new {@link MatrixCursor} containing the {@link CoalescedAnnotatedCallLog} rows to
- * display
- */
- @WorkerThread
- @NonNull
- Cursor coalesce(@NonNull Cursor allAnnotatedCallLogRowsSortedByTimestampDesc) {
- Assert.isWorkerThread();
-
- // Note: This method relies on rowsShouldBeCombined to determine which rows should be combined,
- // but delegates to data sources to actually aggregate column values.
-
- MatrixCursor allCoalescedRowsMatrixCursor =
- new MatrixCursor(
- CoalescedAnnotatedCallLog.ALL_COLUMNS,
- Assert.isNotNull(allAnnotatedCallLogRowsSortedByTimestampDesc).getCount());
-
- if (allAnnotatedCallLogRowsSortedByTimestampDesc.moveToFirst()) {
- int coalescedRowId = 0;
-
- List<ContentValues> currentRowGroup = new ArrayList<>();
-
- do {
- ContentValues currentRow = new ContentValues();
- DatabaseUtils.cursorRowToContentValues(
- allAnnotatedCallLogRowsSortedByTimestampDesc, currentRow);
-
- if (currentRowGroup.isEmpty()) {
- currentRowGroup.add(currentRow);
- continue;
- }
-
- ContentValues previousRow = currentRowGroup.get(currentRowGroup.size() - 1);
-
- if (!rowsShouldBeCombined(previousRow, currentRow)) {
- ContentValues coalescedRow = coalesceRowsForAllDataSources(currentRowGroup);
- coalescedRow.put(CoalescedAnnotatedCallLog.NUMBER_CALLS, currentRowGroup.size());
- addContentValuesToMatrixCursor(
- coalescedRow, allCoalescedRowsMatrixCursor, coalescedRowId++);
- currentRowGroup.clear();
- }
- currentRowGroup.add(currentRow);
- } while (allAnnotatedCallLogRowsSortedByTimestampDesc.moveToNext());
-
- // Deal with leftover rows.
- ContentValues coalescedRow = coalesceRowsForAllDataSources(currentRowGroup);
- coalescedRow.put(CoalescedAnnotatedCallLog.NUMBER_CALLS, currentRowGroup.size());
- addContentValuesToMatrixCursor(coalescedRow, allCoalescedRowsMatrixCursor, coalescedRowId);
- }
- return allCoalescedRowsMatrixCursor;
- }
-
- /**
- * @param row1 a row from {@link AnnotatedCallLog}
- * @param row2 a row from {@link AnnotatedCallLog}
- */
- private static boolean rowsShouldBeCombined(ContentValues row1, ContentValues row2) {
- // TODO: Real implementation.
- return row1.get(AnnotatedCallLog.TIMESTAMP).equals(row2.get(AnnotatedCallLog.TIMESTAMP));
- }
-
- /**
- * Delegates to data sources to aggregate individual columns to create a new coalesced row.
- *
- * @param individualRows {@link AnnotatedCallLog} rows sorted by timestamp descending
- * @return a {@link CoalescedAnnotatedCallLog} row
- */
- private ContentValues coalesceRowsForAllDataSources(List<ContentValues> individualRows) {
- ContentValues coalescedValues = new ContentValues();
- for (CallLogDataSource dataSource : dataSources.getDataSourcesIncludingSystemCallLog()) {
- coalescedValues.putAll(dataSource.coalesce(individualRows));
- }
- return coalescedValues;
- }
-
- /**
- * @param contentValues a {@link CoalescedAnnotatedCallLog} row
- * @param matrixCursor represents {@link CoalescedAnnotatedCallLog}
- */
- private static void addContentValuesToMatrixCursor(
- ContentValues contentValues, MatrixCursor matrixCursor, int rowId) {
- MatrixCursor.RowBuilder rowBuilder = matrixCursor.newRow();
- rowBuilder.add(CoalescedAnnotatedCallLog._ID, rowId);
- for (Map.Entry<String, Object> entry : contentValues.valueSet()) {
- rowBuilder.add(entry.getKey(), entry.getValue());
- }
- }
-}
diff --git a/java/com/android/dialer/calllog/database/MutationApplier.java b/java/com/android/dialer/calllog/database/MutationApplier.java
deleted file mode 100644
index 21c8a507d..000000000
--- a/java/com/android/dialer/calllog/database/MutationApplier.java
+++ /dev/null
@@ -1,105 +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.database;
-
-import android.content.ContentProviderOperation;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.OperationApplicationException;
-import android.os.RemoteException;
-import android.support.annotation.WorkerThread;
-import android.text.TextUtils;
-import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract;
-import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog;
-import com.android.dialer.calllog.datasources.CallLogMutations;
-import com.android.dialer.common.Assert;
-import com.android.dialer.common.LogUtil;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Map.Entry;
-import javax.inject.Inject;
-
-/** Applies {@link CallLogMutations} to the annotated call log. */
-public class MutationApplier {
-
- @Inject
- MutationApplier() {}
-
- /** Applies the provided {@link CallLogMutations} to the annotated call log. */
- @WorkerThread
- public void applyToDatabase(CallLogMutations mutations, Context appContext)
- throws RemoteException, OperationApplicationException {
- Assert.isWorkerThread();
-
- if (mutations.isEmpty()) {
- return;
- }
-
- ArrayList<ContentProviderOperation> operations = new ArrayList<>();
-
- if (!mutations.getInserts().isEmpty()) {
- LogUtil.i(
- "CallLogMutations.applyToDatabase", "inserting %d rows", mutations.getInserts().size());
- for (Entry<Long, ContentValues> entry : mutations.getInserts().entrySet()) {
- long id = entry.getKey();
- ContentValues contentValues = entry.getValue();
- operations.add(
- ContentProviderOperation.newInsert(
- ContentUris.withAppendedId(AnnotatedCallLog.CONTENT_URI, id))
- .withValues(contentValues)
- .build());
- }
- }
-
- if (!mutations.getUpdates().isEmpty()) {
- LogUtil.i(
- "CallLogMutations.applyToDatabase", "updating %d rows", mutations.getUpdates().size());
- for (Entry<Long, ContentValues> entry : mutations.getUpdates().entrySet()) {
- long id = entry.getKey();
- ContentValues contentValues = entry.getValue();
- operations.add(
- ContentProviderOperation.newUpdate(
- ContentUris.withAppendedId(AnnotatedCallLog.CONTENT_URI, id))
- .withValues(contentValues)
- .build());
- }
- }
-
- if (!mutations.getDeletes().isEmpty()) {
- LogUtil.i(
- "CallLogMutations.applyToDatabase", "deleting %d rows", mutations.getDeletes().size());
- String[] questionMarks = new String[mutations.getDeletes().size()];
- Arrays.fill(questionMarks, "?");
-
- String whereClause =
- (AnnotatedCallLog._ID + " in (") + TextUtils.join(",", questionMarks) + ")";
-
- String[] whereArgs = new String[mutations.getDeletes().size()];
- int i = 0;
- for (long id : mutations.getDeletes()) {
- whereArgs[i++] = String.valueOf(id);
- }
-
- operations.add(
- ContentProviderOperation.newDelete(AnnotatedCallLog.CONTENT_URI)
- .withSelection(whereClause, whereArgs)
- .build());
- }
-
- appContext.getContentResolver().applyBatch(AnnotatedCallLogContract.AUTHORITY, operations);
- }
-}
diff --git a/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java b/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java
deleted file mode 100644
index 8b3b0a852..000000000
--- a/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java
+++ /dev/null
@@ -1,114 +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.database.contract;
-
-import android.net.Uri;
-import android.provider.BaseColumns;
-import com.android.dialer.constants.Constants;
-import java.util.Arrays;
-
-/** Contract for the AnnotatedCallLog content provider. */
-public class AnnotatedCallLogContract {
- public static final String AUTHORITY = Constants.get().getAnnotatedCallLogProviderAuthority();
-
- public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
-
- /**
- * Columns shared by {@link AnnotatedCallLog} and {@link CoalescedAnnotatedCallLog}.
- *
- * <p>When adding columns be sure to update {@link #ALL_COMMON_COLUMNS}.
- */
- interface CommonColumns extends BaseColumns {
-
- /**
- * Timestamp of the entry, in milliseconds.
- *
- * <p>Type: INTEGER (long)
- */
- String TIMESTAMP = "timestamp";
-
- /**
- * Name to display for the entry.
- *
- * <p>Type: TEXT
- */
- String CONTACT_NAME = "contact_name";
-
- String[] ALL_COMMON_COLUMNS = new String[] {_ID, TIMESTAMP, CONTACT_NAME};
- }
-
- /**
- * AnnotatedCallLog table.
- *
- * <p>This contains all of the non-coalesced call log entries.
- */
- public static final class AnnotatedCallLog implements CommonColumns {
-
- public static final String TABLE = "AnnotatedCallLog";
-
- /** The content URI for this table. */
- public static final Uri CONTENT_URI =
- Uri.withAppendedPath(AnnotatedCallLogContract.CONTENT_URI, TABLE);
-
- /** The MIME type of a {@link android.content.ContentProvider#getType(Uri)} single entry. */
- public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/annotated_call_log";
- }
-
- /**
- * Coalesced view of the AnnotatedCallLog table.
- *
- * <p>This is an in-memory view of the {@link AnnotatedCallLog} with some adjacent entries
- * collapsed.
- *
- * <p>When adding columns be sure to update {@link #COLUMNS_ONLY_IN_COALESCED_CALL_LOG}.
- */
- public static final class CoalescedAnnotatedCallLog implements CommonColumns {
-
- public static final String TABLE = "CoalescedAnnotatedCallLog";
-
- /** The content URI for this table. */
- public static final Uri CONTENT_URI =
- Uri.withAppendedPath(AnnotatedCallLogContract.CONTENT_URI, TABLE);
-
- /** The MIME type of a {@link android.content.ContentProvider#getType(Uri)} single entry. */
- public static final String CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/coalesced_annotated_call_log";
-
- /**
- * Number of AnnotatedCallLog rows represented by this CoalescedAnnotatedCallLog row.
- *
- * <p>Type: INTEGER
- */
- public static final String NUMBER_CALLS = "number_calls";
-
- /**
- * Columns that are only in the {@link CoalescedAnnotatedCallLog} but not the {@link
- * AnnotatedCallLog}.
- */
- private static final String[] COLUMNS_ONLY_IN_COALESCED_CALL_LOG = new String[] {NUMBER_CALLS};
-
- /** All columns in the {@link CoalescedAnnotatedCallLog}. */
- public static final String[] ALL_COLUMNS =
- concat(ALL_COMMON_COLUMNS, COLUMNS_ONLY_IN_COALESCED_CALL_LOG);
- }
-
- private static String[] concat(String[] first, String[] second) {
- String[] result = Arrays.copyOf(first, first.length + second.length);
- System.arraycopy(second, 0, result, first.length, second.length);
- return result;
- }
-}
diff --git a/java/com/android/dialer/calllog/datasources/CallLogDataSource.java b/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
index 3fff3ba53..13d0b842d 100644
--- a/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
@@ -16,39 +16,13 @@
package com.android.dialer.calllog.datasources;
-import android.content.ContentValues;
import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.MainThread;
import android.support.annotation.WorkerThread;
-import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract;
-import java.util.List;
+import com.android.dialer.calllog.database.CallLogMutations;
-/**
- * A source of data for one or more columns in the annotated call log.
- *
- * <p>Data sources have three lifecycle operations, which are always called on the same thread and
- * in the same order for a particular "checkDirtyAndRebuild" cycle. However, not all operations are
- * always invoked.
- *
- * <ol>
- * <li>{@link #isDirty(Context)}: Invoked only if the framework doesn't yet know if a rebuild is
- * necessary.
- * <li>{@link #fill(Context, CallLogMutations)}: Invoked only if the framework determined a
- * rebuild is necessary.
- * <li>{@link #onSuccessfulFill(Context)}: Invoked if and only if fill was previously called and
- * the mutations provided by the previous fill operation succeeded in being applied.
- * </ol>
- *
- * <p>Because {@link #isDirty(Context)} is not always invoked, {@link #fill(Context,
- * CallLogMutations)} shouldn't rely on any state saved during {@link #isDirty(Context)}. It
- * <em>is</em> safe to assume that {@link #onSuccessfulFill(Context)} refers to the previous fill
- * operation.
- *
- * <p>The same data source objects may be reused across multiple checkDirtyAndRebuild cycles, so
- * implementors should take care to clear any internal state at the start of a new cycle.
- *
- * <p>{@link #coalesce(List)} may be called from any worker thread at any time.
- */
+/** A source of data for one or more columns in the annotated call log. */
public interface CallLogDataSource {
/**
@@ -61,8 +35,6 @@ public interface CallLogDataSource {
* <p>Most implementations of this method will rely on some sort of last modified timestamp. If it
* is impossible for a data source to be modified without the dialer application being notified,
* this method may immediately return false.
- *
- * @see CallLogDataSource class doc for complete lifecyle information
*/
@WorkerThread
boolean isDirty(Context appContext);
@@ -71,39 +43,16 @@ public interface CallLogDataSource {
* Computes the set of mutations necessary to update the annotated call log with respect to this
* data source.
*
- * @see CallLogDataSource class doc for complete lifecyle information
* @param mutations the set of mutations which this method should contribute to. Note that it may
* contain inserts from the system call log, and these inserts should be modified by each data
* source.
*/
@WorkerThread
- void fill(Context appContext, CallLogMutations mutations);
-
- /**
- * Called after database mutations have been applied to all data sources. This is useful for
- * saving state such as the timestamp of the last row processed in an underlying database. Note
- * that all mutations across all data sources are applied in a single transaction.
- *
- * @see CallLogDataSource class doc for complete lifecyle information
- */
- @WorkerThread
- void onSuccessfulFill(Context appContext);
-
- /**
- * Combines raw annotated call log rows into a single coalesced row.
- *
- * <p>May be called by any worker thread at any time so implementations should take care to be
- * threadsafe. (Ideally no state should be required to implement this.)
- *
- * @param individualRowsSortedByTimestampDesc group of fully populated rows from {@link
- * AnnotatedCallLogContract.AnnotatedCallLog} which need to be combined for display purposes.
- * This method should not modify this list.
- * @return a partial {@link AnnotatedCallLogContract.CoalescedAnnotatedCallLog} row containing
- * only columns which this data source is responsible for, which is the result of aggregating
- * {@code individualRowsSortedByTimestampDesc}.
- */
- @WorkerThread
- ContentValues coalesce(List<ContentValues> individualRowsSortedByTimestampDesc);
+ void fill(
+ Context appContext,
+ SQLiteDatabase readableDatabase,
+ long lastRebuildTimeMillis,
+ CallLogMutations mutations);
@MainThread
void registerContentObservers(
diff --git a/java/com/android/dialer/calllog/datasources/CallLogMutations.java b/java/com/android/dialer/calllog/datasources/CallLogMutations.java
deleted file mode 100644
index 148601d68..000000000
--- a/java/com/android/dialer/calllog/datasources/CallLogMutations.java
+++ /dev/null
@@ -1,110 +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.datasources;
-
-import android.content.ContentValues;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import com.android.dialer.common.Assert;
-
-/** A collection of mutations to the annotated call log. */
-public final class CallLogMutations {
-
- private final ArrayMap<Long, ContentValues> inserts = new ArrayMap<>();
- private final ArrayMap<Long, ContentValues> updates = new ArrayMap<>();
- private final ArraySet<Long> deletes = new ArraySet<>();
-
- /**
- * @param contentValues an entire row not including the ID
- * @throws IllegalStateException if this {@link CallLogMutations} already contains an insert,
- * update, or delete with the provided id
- */
- public void insert(long id, ContentValues contentValues) {
- Assert.checkArgument(!inserts.containsKey(id), "Can't insert row already scheduled for insert");
- Assert.checkArgument(!updates.containsKey(id), "Can't insert row scheduled for update");
- Assert.checkArgument(!deletes.contains(id), "Can't insert row scheduled for delete");
-
- inserts.put(id, contentValues);
- }
-
- /**
- * Stores a database update using the provided ID and content values. If this {@link
- * CallLogMutations} object already contains an update with the specified ID, the existing content
- * values are merged with the provided ones, with the provided ones overwriting the existing ones
- * for values with the same key.
- *
- * @param contentValues the specific columns to update, not including the ID.
- * @throws IllegalStateException if this {@link CallLogMutations} already contains an insert or
- * delete with the provided id
- */
- public void update(long id, ContentValues contentValues) {
- Assert.checkArgument(!inserts.containsKey(id), "Can't update row scheduled for insert");
- Assert.checkArgument(!deletes.contains(id), "Can't delete row scheduled for delete");
-
- ContentValues existingContentValues = updates.get(id);
- if (existingContentValues != null) {
- existingContentValues.putAll(contentValues);
- } else {
- updates.put(id, contentValues);
- }
- }
-
- /**
- * @throws IllegalStateException if this {@link CallLogMutations} already contains an insert,
- * update, or delete with the provided id
- */
- public void delete(long id) {
- Assert.checkArgument(!inserts.containsKey(id), "Can't delete row scheduled for insert");
- Assert.checkArgument(!updates.containsKey(id), "Can't delete row scheduled for update");
- Assert.checkArgument(!deletes.contains(id), "Can't delete row already scheduled for delete");
-
- deletes.add(id);
- }
-
- public boolean isEmpty() {
- return inserts.isEmpty() && updates.isEmpty() && deletes.isEmpty();
- }
-
- /**
- * Get the pending inserts.
- *
- * @return the pending inserts where the key is the annotated call log database ID and the values
- * are values to be inserted (not including the ID)
- */
- public ArrayMap<Long, ContentValues> getInserts() {
- return inserts;
- }
-
- /**
- * Get the pending updates.
- *
- * @return the pending updates where the key is the annotated call log database ID and the values
- * are values to be updated (not including the ID)
- */
- public ArrayMap<Long, ContentValues> getUpdates() {
- return updates;
- }
-
- /**
- * Get the pending deletes.
- *
- * @return the annotated call log database IDs corresponding to the rows to be deleted
- */
- public ArraySet<Long> getDeletes() {
- return deletes;
- }
-}
diff --git a/java/com/android/dialer/calllog/datasources/contacts/ContactsDataSource.java b/java/com/android/dialer/calllog/datasources/contacts/ContactsDataSource.java
index e9538daab..355940f6a 100644
--- a/java/com/android/dialer/calllog/datasources/contacts/ContactsDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/contacts/ContactsDataSource.java
@@ -16,16 +16,13 @@
package com.android.dialer.calllog.datasources.contacts;
-import android.content.ContentValues;
import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.MainThread;
import android.support.annotation.WorkerThread;
-import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog;
+import com.android.dialer.calllog.database.CallLogMutations;
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.common.Assert;
-import java.util.List;
import javax.inject.Inject;
/** Responsible for maintaining the contacts related columns in the annotated call log. */
@@ -47,24 +44,13 @@ public final class ContactsDataSource implements CallLogDataSource {
@Override
public void fill(
Context appContext,
+ SQLiteDatabase readableDatabase,
+ long lastRebuildTimeMillis,
CallLogMutations mutations) {
Assert.isWorkerThread();
// TODO: Implementation.
}
- @Override
- public void onSuccessfulFill(Context appContext) {
- // TODO: Implementation.
- }
-
- @Override
- public ContentValues coalesce(List<ContentValues> individualRowsSortedByTimestampDesc) {
- // TODO: Implementation.
- return new RowCombiner(individualRowsSortedByTimestampDesc)
- .useSingleValueString(AnnotatedCallLog.CONTACT_NAME)
- .combine();
- }
-
@MainThread
@Override
public void registerContentObservers(
diff --git a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
index be2df6043..ea6663fbe 100644
--- a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
@@ -16,49 +16,28 @@
package com.android.dialer.calllog.datasources.systemcalllog;
-import android.Manifest.permission;
-import android.annotation.TargetApi;
-import android.content.ContentValues;
import android.content.Context;
import android.database.ContentObserver;
-import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
-import android.os.Build;
import android.os.Handler;
-import android.preference.PreferenceManager;
import android.provider.CallLog;
-import android.provider.CallLog.Calls;
import android.support.annotation.MainThread;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
import android.support.annotation.WorkerThread;
-import android.text.TextUtils;
-import android.util.ArraySet;
-import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog;
+import com.android.dialer.calllog.database.CallLogMutations;
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.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.ThreadUtil;
import com.android.dialer.util.PermissionsUtil;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
import javax.inject.Inject;
/**
* Responsible for defining the rows in the annotated call log and maintaining the columns in it
* which are derived from the system call log.
*/
-@SuppressWarnings("MissingPermission")
public class SystemCallLogDataSource implements CallLogDataSource {
- @VisibleForTesting
- static final String PREF_LAST_TIMESTAMP_PROCESSED = "systemCallLogLastTimestampProcessed";
-
- @Nullable private Long lastTimestampProcessed;
-
@Inject
public SystemCallLogDataSource() {}
@@ -68,8 +47,6 @@ public class SystemCallLogDataSource implements CallLogDataSource {
Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
Assert.isMainThread();
- LogUtil.enterBlock("SystemCallLogDataSource.registerContentObservers");
-
if (!PermissionsUtil.hasCallLogReadPermissions(appContext)) {
LogUtil.i("SystemCallLogDataSource.registerContentObservers", "no call log permissions");
return;
@@ -100,185 +77,17 @@ public class SystemCallLogDataSource implements CallLogDataSource {
@WorkerThread
@Override
- public void fill(Context appContext, CallLogMutations mutations) {
+ public void fill(
+ Context appContext,
+ SQLiteDatabase readableDatabase,
+ long lastRebuildTimeMillis,
+ CallLogMutations mutations) {
Assert.isWorkerThread();
- lastTimestampProcessed = null;
-
- if (!PermissionsUtil.hasPermission(appContext, permission.READ_CALL_LOG)) {
- LogUtil.i("SystemCallLogDataSource.fill", "no call log permissions");
- return;
- }
-
// This data source should always run first so the mutations should always be empty.
- Assert.checkArgument(mutations.isEmpty());
-
- Set<Long> annotatedCallLogIds = getAnnotatedCallLogIds(appContext);
-
- LogUtil.i(
- "SystemCallLogDataSource.fill",
- "found %d existing annotated call log ids",
- annotatedCallLogIds.size());
-
- handleInsertsAndUpdates(appContext, mutations, annotatedCallLogIds);
- handleDeletes(appContext, annotatedCallLogIds, mutations);
- }
-
- @WorkerThread
- @Override
- public void onSuccessfulFill(Context appContext) {
- // If a fill operation was a no-op, lastTimestampProcessed could still be null.
- if (lastTimestampProcessed != null) {
- PreferenceManager.getDefaultSharedPreferences(appContext)
- .edit()
- .putLong(PREF_LAST_TIMESTAMP_PROCESSED, lastTimestampProcessed)
- .commit();
- }
- }
-
- @Override
- public ContentValues coalesce(List<ContentValues> individualRowsSortedByTimestampDesc) {
- // TODO: Complete implementation.
- return new RowCombiner(individualRowsSortedByTimestampDesc)
- .useMostRecentLong(AnnotatedCallLog.TIMESTAMP)
- .combine();
- }
-
- @TargetApi(Build.VERSION_CODES.M) // Uses try-with-resources
- private void handleInsertsAndUpdates(
- Context appContext, CallLogMutations mutations, Set<Long> existingAnnotatedCallLogIds) {
- long previousTimestampProcessed =
- PreferenceManager.getDefaultSharedPreferences(appContext)
- .getLong(PREF_LAST_TIMESTAMP_PROCESSED, 0L);
-
- try (Cursor cursor =
- appContext
- .getContentResolver()
- .query(
- Calls.CONTENT_URI, // Excludes voicemail
- new String[] {Calls._ID, Calls.DATE, Calls.LAST_MODIFIED},
- Calls.LAST_MODIFIED + " > ?",
- new String[] {String.valueOf(previousTimestampProcessed)},
- Calls.LAST_MODIFIED + " DESC LIMIT 1000")) {
-
- if (cursor == null) {
- LogUtil.e("SystemCallLogDataSource.handleInsertsAndUpdates", "null cursor");
- return;
- }
-
- LogUtil.i(
- "SystemCallLogDataSource.handleInsertsAndUpdates",
- "found %d entries to insert/update",
- cursor.getCount());
+ Assert.checkState(mutations.isEmpty());
- if (cursor.moveToFirst()) {
- int idColumn = cursor.getColumnIndexOrThrow(Calls._ID);
- int dateColumn = cursor.getColumnIndexOrThrow(Calls.DATE);
- int lastModifiedColumn = cursor.getColumnIndexOrThrow(Calls.LAST_MODIFIED);
-
- // The cursor orders by LAST_MODIFIED DESC, so the first result is the most recent timestamp
- // processed.
- lastTimestampProcessed = cursor.getLong(lastModifiedColumn);
- do {
- long id = cursor.getLong(idColumn);
- long date = cursor.getLong(dateColumn);
-
- ContentValues contentValues = new ContentValues();
- contentValues.put(AnnotatedCallLog.TIMESTAMP, date);
-
- if (existingAnnotatedCallLogIds.contains(id)) {
- mutations.update(id, contentValues);
- } else {
- mutations.insert(id, contentValues);
- }
- } while (cursor.moveToNext());
- } // else no new results, do nothing.
- }
- }
-
- private static void handleDeletes(
- Context appContext, Set<Long> existingAnnotatedCallLogIds, CallLogMutations mutations) {
- Set<Long> systemCallLogIds =
- getIdsFromSystemCallLogThatMatch(appContext, existingAnnotatedCallLogIds);
- LogUtil.i(
- "SystemCallLogDataSource.handleDeletes",
- "found %d entries in system call log",
- systemCallLogIds.size());
- Set<Long> idsInAnnotatedCallLogNoLongerInSystemCallLog = new ArraySet<>();
- idsInAnnotatedCallLogNoLongerInSystemCallLog.addAll(existingAnnotatedCallLogIds);
- idsInAnnotatedCallLogNoLongerInSystemCallLog.removeAll(systemCallLogIds);
-
- LogUtil.i(
- "SystemCallLogDataSource.handleDeletes",
- "found %d call log entries to remove",
- idsInAnnotatedCallLogNoLongerInSystemCallLog.size());
-
- for (long id : idsInAnnotatedCallLogNoLongerInSystemCallLog) {
- mutations.delete(id);
- }
- }
-
- @TargetApi(Build.VERSION_CODES.M) // Uses try-with-resources
- private static Set<Long> getAnnotatedCallLogIds(Context appContext) {
- ArraySet<Long> ids = new ArraySet<>();
-
- try (Cursor cursor =
- appContext
- .getContentResolver()
- .query(
- AnnotatedCallLog.CONTENT_URI,
- new String[] {AnnotatedCallLog._ID},
- null,
- null,
- null)) {
-
- if (cursor == null) {
- LogUtil.e("SystemCallLogDataSource.getAnnotatedCallLogIds", "null cursor");
- return ids;
- }
-
- if (cursor.moveToFirst()) {
- int idColumn = cursor.getColumnIndexOrThrow(AnnotatedCallLog._ID);
- do {
- ids.add(cursor.getLong(idColumn));
- } while (cursor.moveToNext());
- }
- }
- return ids;
- }
-
- @TargetApi(Build.VERSION_CODES.M) // Uses try-with-resources
- private static Set<Long> getIdsFromSystemCallLogThatMatch(
- Context appContext, Set<Long> matchingIds) {
- ArraySet<Long> ids = new ArraySet<>();
-
- String[] questionMarks = new String[matchingIds.size()];
- Arrays.fill(questionMarks, "?");
- String whereClause = (Calls._ID + " in (") + TextUtils.join(",", questionMarks) + ")";
- String[] whereArgs = new String[matchingIds.size()];
- int i = 0;
- for (long id : matchingIds) {
- whereArgs[i++] = String.valueOf(id);
- }
-
- try (Cursor cursor =
- appContext
- .getContentResolver()
- .query(Calls.CONTENT_URI, new String[] {Calls._ID}, whereClause, whereArgs, null)) {
-
- if (cursor == null) {
- LogUtil.e("SystemCallLogDataSource.getIdsFromSystemCallLog", "null cursor");
- return ids;
- }
-
- if (cursor.moveToFirst()) {
- int idColumn = cursor.getColumnIndexOrThrow(Calls._ID);
- do {
- ids.add(cursor.getLong(idColumn));
- } while (cursor.moveToNext());
- }
- return ids;
- }
+ // TODO: Implementation.
}
private static class CallLogObserver extends ContentObserver {
diff --git a/java/com/android/dialer/calllog/datasources/util/RowCombiner.java b/java/com/android/dialer/calllog/datasources/util/RowCombiner.java
deleted file mode 100644
index 0c7be1e27..000000000
--- a/java/com/android/dialer/calllog/datasources/util/RowCombiner.java
+++ /dev/null
@@ -1,53 +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.datasources.util;
-
-import android.content.ContentValues;
-import com.android.dialer.common.Assert;
-import java.util.Iterator;
-import java.util.List;
-
-/** Convenience class for aggregating row values. */
-public class RowCombiner {
- private final List<ContentValues> individualRowsSortedByTimestampDesc;
- private final ContentValues combinedRow = new ContentValues();
-
- public RowCombiner(List<ContentValues> individualRowsSortedByTimestampDesc) {
- Assert.checkArgument(!individualRowsSortedByTimestampDesc.isEmpty());
- this.individualRowsSortedByTimestampDesc = individualRowsSortedByTimestampDesc;
- }
-
- /** Use the most recent value for the specified column. */
- public RowCombiner useMostRecentLong(String columnName) {
- combinedRow.put(columnName, individualRowsSortedByTimestampDesc.get(0).getAsLong(columnName));
- return this;
- }
-
- /** Asserts that all column values for the given column name are the same, and uses it. */
- public RowCombiner useSingleValueString(String columnName) {
- Iterator<ContentValues> iterator = individualRowsSortedByTimestampDesc.iterator();
- String singleValue = iterator.next().getAsString(columnName);
- while (iterator.hasNext()) {
- Assert.checkState(iterator.next().getAsString(columnName).equals(singleValue));
- }
- combinedRow.put(columnName, singleValue);
- return this;
- }
-
- public ContentValues combine() {
- return combinedRow;
- }
-}
diff --git a/java/com/android/dialer/calllog/testing/FakeCallLogApplication.java b/java/com/android/dialer/calllog/testing/FakeCallLogApplication.java
deleted file mode 100644
index cb2240539..000000000
--- a/java/com/android/dialer/calllog/testing/FakeCallLogApplication.java
+++ /dev/null
@@ -1,39 +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.testing;
-
-import android.app.Application;
-import com.android.dialer.calllog.CallLogModule;
-import com.android.dialer.calllog.database.CallLogDatabaseComponent;
-import com.android.dialer.inject.HasRootComponent;
-import dagger.Component;
-import javax.inject.Singleton;
-
-/**
- * Fake application for call log robolectric tests which uses all real bindings but doesn't require
- * tests to depend on and use all of DialerApplication.
- */
-public final class FakeCallLogApplication extends Application implements HasRootComponent {
-
- @Override
- public Object component() {
- return DaggerFakeCallLogApplication_FakeComponent.create();
- }
-
- @Singleton
- @Component(modules = CallLogModule.class)
- interface FakeComponent extends CallLogDatabaseComponent.HasComponent {}
-}
diff --git a/java/com/android/dialer/calllog/ui/AndroidManifest.xml b/java/com/android/dialer/calllog/ui/AndroidManifest.xml
index eaf71aba8..228167749 100644
--- a/java/com/android/dialer/calllog/ui/AndroidManifest.xml
+++ b/java/com/android/dialer/calllog/ui/AndroidManifest.xml
@@ -13,4 +13,4 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<manifest package="com.android.dialer.calllog.ui"/>
+<manifest package="com.android.dialer.calllog"/>
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..cd8622e80
--- /dev/null
+++ b/java/com/android/dialer/calllog/ui/AnnotatedCallLogCursorLoader.java
@@ -0,0 +1,48 @@
+/*
+ * 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.annotation.TargetApi;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.Build;
+import com.android.dialer.calllog.database.AnnotatedCallLog;
+import com.android.dialer.calllog.database.AnnotatedCallLog.Columns;
+
+/** CursorLoader which reads the annotated call log. */
+class AnnotatedCallLogCursorLoader extends CursorLoader {
+
+ AnnotatedCallLogCursorLoader(Context context) {
+ super(context);
+ }
+
+ @TargetApi(Build.VERSION_CODES.M) // Uses try-with-resources
+ @Override
+ public Cursor loadInBackground() {
+ try (SQLiteDatabase readableDatabase = AnnotatedCallLog.getReadableDatabase(getContext())) {
+ return readableDatabase.rawQuery(
+ "SELECT * FROM "
+ + AnnotatedCallLog.TABLE_NAME
+ + " ORDER BY "
+ + Columns.TIMESTAMP
+ + " DESC",
+ null /* selectionArgs */);
+ }
+ }
+}
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java b/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java
deleted file mode 100644
index f9ab21cb3..000000000
--- a/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java
+++ /dev/null
@@ -1,53 +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.database.Cursor;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.CoalescedAnnotatedCallLog;
-
-/** {@link RecyclerView.Adapter} for the new call log fragment. */
-final class NewCallLogAdapter extends RecyclerView.Adapter<NewCallLogViewHolder> {
-
- private final Cursor cursor;
- private final int timestampIndex;
-
- NewCallLogAdapter(Cursor cursor) {
- this.cursor = cursor;
- timestampIndex = cursor.getColumnIndexOrThrow(CoalescedAnnotatedCallLog.TIMESTAMP);
- }
-
- @Override
- public NewCallLogViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
- return new NewCallLogViewHolder(
- LayoutInflater.from(viewGroup.getContext())
- .inflate(R.layout.new_call_log_entry, viewGroup, false));
- }
-
- @Override
- public void onBindViewHolder(NewCallLogViewHolder viewHolder, int position) {
- cursor.moveToPosition(position);
- long timestamp = cursor.getLong(timestampIndex);
- viewHolder.bind(timestamp);
- }
-
- @Override
- public int getItemCount() {
- return cursor.getCount();
- }
-}
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
index 89ed52fd7..b8f2b1326 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
@@ -17,30 +17,30 @@ package com.android.dialer.calllog.ui;
import android.app.Fragment;
import android.app.LoaderManager.LoaderCallbacks;
-import android.content.CursorLoader;
+import android.content.Context;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
-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 android.widget.CursorAdapter;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+import android.widget.TextView;
import com.android.dialer.calllog.CallLogComponent;
import com.android.dialer.calllog.CallLogFramework;
import com.android.dialer.calllog.CallLogFramework.CallLogUi;
-import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.CoalescedAnnotatedCallLog;
+import com.android.dialer.calllog.database.AnnotatedCallLog.Columns;
import com.android.dialer.common.LogUtil;
-import com.android.dialer.common.concurrent.DialerExecutor;
-import com.android.dialer.common.concurrent.DialerExecutorComponent;
-import com.android.dialer.common.concurrent.DialerExecutorFactory;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
/** 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> {
- private DialerExecutor<Boolean> refreshAnnotatedCallLogTask;
- private RecyclerView recyclerView;
+ private CursorAdapter cursorAdapter;
public NewCallLogFragment() {
LogUtil.enterBlock("NewCallLogFragment.NewCallLogFragment");
@@ -52,27 +52,8 @@ public final class NewCallLogFragment extends Fragment
LogUtil.enterBlock("NewCallLogFragment.onCreate");
- CallLogComponent component = CallLogComponent.get(getContext());
- CallLogFramework callLogFramework = component.callLogFramework();
+ CallLogFramework callLogFramework = CallLogComponent.get(getContext()).callLogFramework();
callLogFramework.attachUi(this);
-
- DialerExecutorFactory dialerExecutorFactory =
- DialerExecutorComponent.get(getContext()).dialerExecutorFactory();
-
- refreshAnnotatedCallLogTask =
- dialerExecutorFactory
- .createUiTaskBuilder(
- getFragmentManager(),
- "NewCallLogFragment.refreshAnnotatedCallLog",
- component.getRefreshAnnotatedCallLogWorker())
- .build();
- }
-
- @Override
- public void onStart() {
- super.onStart();
-
- LogUtil.enterBlock("NewCallLogFragment.onStart");
}
@Override
@@ -83,9 +64,6 @@ public final class NewCallLogFragment extends Fragment
CallLogFramework callLogFramework = CallLogComponent.get(getContext()).callLogFramework();
callLogFramework.attachUi(this);
-
- // TODO: Consider doing this when fragment becomes visible.
- checkAnnotatedCallLogDirtyAndRefreshIfNecessary();
}
@Override
@@ -104,44 +82,57 @@ public final class NewCallLogFragment extends Fragment
LogUtil.enterBlock("NewCallLogFragment.onCreateView");
View view = inflater.inflate(R.layout.new_call_log_fragment, container, false);
- recyclerView = view.findViewById(R.id.new_call_log_recycler_view);
+ ListView listView = (ListView) view.findViewById(R.id.list);
- getLoaderManager().restartLoader(0, null, this);
+ this.cursorAdapter =
+ new MyCursorAdapter(
+ getContext(),
+ R.layout.new_call_log_entry,
+ null /* cursor */,
+ new String[] {Columns.TIMESTAMP, Columns.CONTACT_NAME},
+ new int[] {R.id.timestamp, R.id.contact_name},
+ 0);
+ listView.setAdapter(cursorAdapter);
- return view;
- }
+ getLoaderManager().initLoader(0, null, this);
- private void checkAnnotatedCallLogDirtyAndRefreshIfNecessary() {
- LogUtil.enterBlock("NewCallLogFragment.checkAnnotatedCallLogDirtyAndRefreshIfNecessary");
- refreshAnnotatedCallLogTask.executeSerial(false /* skipDirtyCheck */);
+ return view;
}
@Override
public void invalidateUi() {
LogUtil.enterBlock("NewCallLogFragment.invalidateUi");
- refreshAnnotatedCallLogTask.executeSerial(true /* skipDirtyCheck */);
+ // TODO: Implementation.
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- LogUtil.enterBlock("NewCallLogFragment.onCreateLoader");
- // CoalescedAnnotatedCallLog requires that all params be null.
- return new CursorLoader(
- getContext(), CoalescedAnnotatedCallLog.CONTENT_URI, null, null, null, null);
+ // TODO: This is sort of weird, do we need to implement a content provider?
+ return new AnnotatedCallLogCursorLoader(getContext());
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor newCursor) {
- LogUtil.enterBlock("NewCallLogFragment.onLoadFinished");
-
- // TODO: Handle empty cursor by showing empty view.
- recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
- recyclerView.setAdapter(new NewCallLogAdapter(newCursor));
+ cursorAdapter.swapCursor(newCursor);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
- LogUtil.enterBlock("NewCallLogFragment.onLoaderReset");
- recyclerView.setAdapter(null);
+ cursorAdapter.swapCursor(null);
+ }
+
+ private static class MyCursorAdapter extends SimpleCursorAdapter {
+
+ MyCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
+ super(context, layout, c, from, to, flags);
+ }
+
+ @Override
+ public void setViewText(TextView view, String text) {
+ if (view.getId() == R.id.timestamp) {
+ text = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US).format(Long.valueOf(text));
+ }
+ view.setText(text);
+ }
}
}
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java b/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
deleted file mode 100644
index 4c459e123..000000000
--- a/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
+++ /dev/null
@@ -1,43 +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.support.v7.widget.RecyclerView;
-import android.view.View;
-import android.widget.TextView;
-import java.text.SimpleDateFormat;
-import java.util.Locale;
-
-/** {@link RecyclerView.ViewHolder} for the new call log. */
-final class NewCallLogViewHolder extends RecyclerView.ViewHolder {
-
- // TODO: Format correctly using current locale.
- private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
-
- private final TextView contactNameView;
- private final TextView timestampView;
-
- NewCallLogViewHolder(View view) {
- super(view);
- contactNameView = view.findViewById(R.id.contact_name);
- timestampView = view.findViewById(R.id.timestamp);
- }
-
- void bind(long timestamp) {
- contactNameView.setText("Contact Name Placeholder");
- timestampView.setText(dateFormat.format(timestamp));
- }
-}
diff --git a/java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml b/java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml
index 99797fab4..ee3efd002 100644
--- a/java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml
+++ b/java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml
@@ -18,20 +18,16 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="8dp"
- android:orientation="vertical">
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
<TextView
- android:id="@+id/contact_name"
+ android:id="@+id/timestamp"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@style/PrimaryText"/>
+ android:layout_height="wrap_content"/>
<TextView
- android:id="@+id/timestamp"
+ android:id="@+id/contact_name"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@style/SecondaryText"/>
-
+ android:layout_height="wrap_content"/>
</LinearLayout> \ No newline at end of file
diff --git a/java/com/android/dialer/calllog/ui/res/layout/new_call_log_fragment.xml b/java/com/android/dialer/calllog/ui/res/layout/new_call_log_fragment.xml
index e1d8410b6..433dbdd0f 100644
--- a/java/com/android/dialer/calllog/ui/res/layout/new_call_log_fragment.xml
+++ b/java/com/android/dialer/calllog/ui/res/layout/new_call_log_fragment.xml
@@ -15,9 +15,8 @@
~ limitations under the License
-->
-<android.support.v7.widget.RecyclerView
+<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/new_call_log_recycler_view"
+ android:id="@+id/list"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@color/background_dialer_light"/>
+ android:layout_height="match_parent"/>