summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/calllog
diff options
context:
space:
mode:
authorEric Erfanian <erfanian@google.com>2017-05-03 10:27:13 -0700
committerEric Erfanian <erfanian@google.com>2017-05-03 12:01:21 -0700
commit8369df095a73a77b3715f8ae7ba06089cebca4ce (patch)
tree1a45d60921e293c6088efeaf4d9c408456f3e0e2 /java/com/android/dialer/calllog
parentafa29d4a8659eeffc8d92a6216b154f594eeb895 (diff)
This change reflects the Dialer V10 RC00 branch.
RC00 is based on: branch: dialer-android_release_branch/153304843.1 synced to: 153304843 following the instructions at go/dialer-aosp-release. In this release: * Removes final apache sources. * Uses native lite compilation. More drops will follow with subsequent release candidates until we reach our final v10 release, in cadence with our prebuilt drops. Test: TreeHugger, on device Change-Id: Ic9684057230f9b579c777820c746cd21bf45ec0f
Diffstat (limited to 'java/com/android/dialer/calllog')
-rw-r--r--java/com/android/dialer/calllog/CallLogComponent.java37
-rw-r--r--java/com/android/dialer/calllog/CallLogFramework.java117
-rw-r--r--java/com/android/dialer/calllog/CallLogModule.java62
-rw-r--r--java/com/android/dialer/calllog/DataSources.java31
-rw-r--r--java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java183
-rw-r--r--java/com/android/dialer/calllog/database/AnnotatedCallLog.java53
-rw-r--r--java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java58
-rw-r--r--java/com/android/dialer/calllog/database/CallLogMutations.java58
-rw-r--r--java/com/android/dialer/calllog/datasources/CallLogDataSource.java68
-rw-r--r--java/com/android/dialer/calllog/datasources/contacts/ContactsDataSource.java58
-rw-r--r--java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java114
-rw-r--r--java/com/android/dialer/calllog/ui/AndroidManifest.xml16
-rw-r--r--java/com/android/dialer/calllog/ui/AnnotatedCallLogCursorLoader.java48
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogFragment.java138
-rw-r--r--java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml33
-rw-r--r--java/com/android/dialer/calllog/ui/res/layout/new_call_log_fragment.xml22
16 files changed, 1096 insertions, 0 deletions
diff --git a/java/com/android/dialer/calllog/CallLogComponent.java b/java/com/android/dialer/calllog/CallLogComponent.java
new file mode 100644
index 000000000..5cdd2b4d0
--- /dev/null
+++ b/java/com/android/dialer/calllog/CallLogComponent.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+import android.content.Context;
+import com.android.dialer.inject.HasRootComponent;
+import dagger.Subcomponent;
+
+/** Dagger component for the call log package. */
+@Subcomponent
+public abstract class CallLogComponent {
+
+ public abstract CallLogFramework callLogFramework();
+
+ public static CallLogComponent get(Context context) {
+ return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component())
+ .callLogComponent();
+ }
+
+ /** Used to refer to the root application component. */
+ public interface HasComponent {
+ CallLogComponent callLogComponent();
+ }
+}
diff --git a/java/com/android/dialer/calllog/CallLogFramework.java b/java/com/android/dialer/calllog/CallLogFramework.java
new file mode 100644
index 000000000..508413b14
--- /dev/null
+++ b/java/com/android/dialer/calllog/CallLogFramework.java
@@ -0,0 +1,117 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+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.common.Assert;
+import com.android.dialer.common.ConfigProviderBindings;
+import com.android.dialer.common.LogUtil;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Coordinates work across CallLog data sources to detect if the annotated call log is out of date
+ * ("dirty") and update it if necessary.
+ *
+ * <p>All methods should be called on the main thread.
+ */
+@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;
+
+ @Nullable private CallLogUi ui;
+
+ @Inject
+ CallLogFramework(DataSources dataSources) {
+ this.dataSources = dataSources;
+ }
+
+ public boolean isNewCallLogEnabled(Context context) {
+ return ConfigProviderBindings.get(context).getBoolean("enable_new_call_log_tab", false);
+ }
+
+ /** Registers the content observers for all data sources. */
+ public void registerContentObservers(Context appContext) {
+ LogUtil.enterBlock("CallLogFramework.registerContentObservers");
+
+ if (!isNewCallLogEnabled(appContext)) {
+ return;
+ }
+
+ for (CallLogDataSource dataSource : dataSources.getDataSourcesIncludingSystemCallLog()) {
+ dataSource.registerContentObservers(appContext, this);
+ }
+ }
+
+ /**
+ * Attach a UI component to the framework so that it may be notified of changes to the annotated
+ * call log.
+ */
+ public void attachUi(CallLogUi ui) {
+ LogUtil.enterBlock("CallLogFramework.attachUi");
+ this.ui = ui;
+ }
+
+ /**
+ * Detaches the UI from the framework. This should be called when the UI is hidden or destroyed
+ * and no longer needs to be notified of changes to the annotated call log.
+ */
+ public void detachUi() {
+ LogUtil.enterBlock("CallLogFramework.detachUi");
+ this.ui = null;
+ }
+
+ /**
+ * Marks the call log as dirty and notifies any attached UI components. If there are no UI
+ * components currently attached, this is an efficient operation since it is just writing a shared
+ * pref.
+ *
+ * <p>We don't want to actually force a rebuild when there is no UI running because we don't want
+ * to be constantly rebuilding the database when the device is sitting on a desk and receiving a
+ * lot of calls, for example.
+ */
+ @Override
+ @MainThread
+ public void markDirtyAndNotify(Context appContext) {
+ Assert.isMainThread();
+ LogUtil.enterBlock("CallLogFramework.markDirtyAndNotify");
+
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext);
+ sharedPreferences.edit().putBoolean(PREF_FORCE_REBUILD, true).apply();
+
+ if (ui != null) {
+ ui.invalidateUi();
+ }
+ }
+
+ /** Callbacks invoked on listening UI components. */
+ public interface CallLogUi {
+
+ /** Notifies the call log UI that the annotated call log is out of date. */
+ @MainThread
+ void invalidateUi();
+ }
+}
diff --git a/java/com/android/dialer/calllog/CallLogModule.java b/java/com/android/dialer/calllog/CallLogModule.java
new file mode 100644
index 000000000..d7473a75e
--- /dev/null
+++ b/java/com/android/dialer/calllog/CallLogModule.java
@@ -0,0 +1,62 @@
+/*
+ * 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;
+
+import com.android.dialer.calllog.datasources.CallLogDataSource;
+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;
+import java.util.Collections;
+import java.util.List;
+
+/** Dagger module which satisfies call log dependencies. */
+@Module
+public abstract class CallLogModule {
+
+ @Binds
+ abstract DialerExecutorFactory bindDialerExecutorFactory(
+ DefaultDialerExecutorFactory defaultDialerExecutorFactory);
+
+ @Provides
+ static DataSources provideCallLogDataSources(
+ SystemCallLogDataSource systemCallLogDataSource, ContactsDataSource contactsDataSource) {
+ // System call log must be first, see getDataSourcesExcludingSystemCallLog below.
+ List<CallLogDataSource> allDataSources =
+ Collections.unmodifiableList(Arrays.asList(systemCallLogDataSource, contactsDataSource));
+ return new DataSources() {
+ @Override
+ public SystemCallLogDataSource getSystemCallLogDataSource() {
+ return systemCallLogDataSource;
+ }
+
+ @Override
+ public List<CallLogDataSource> getDataSourcesIncludingSystemCallLog() {
+ return allDataSources;
+ }
+
+ @Override
+ public List<CallLogDataSource> getDataSourcesExcludingSystemCallLog() {
+ return allDataSources.subList(1, allDataSources.size());
+ }
+ };
+ }
+}
diff --git a/java/com/android/dialer/calllog/DataSources.java b/java/com/android/dialer/calllog/DataSources.java
new file mode 100644
index 000000000..21d190167
--- /dev/null
+++ b/java/com/android/dialer/calllog/DataSources.java
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+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. */
+interface DataSources {
+
+ SystemCallLogDataSource getSystemCallLogDataSource();
+
+ List<CallLogDataSource> getDataSourcesIncludingSystemCallLog();
+
+ List<CallLogDataSource> getDataSourcesExcludingSystemCallLog();
+}
diff --git a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
new file mode 100644
index 000000000..f9f0c9935
--- /dev/null
+++ b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
@@ -0,0 +1,183 @@
+/*
+ * 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;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.Build;
+import android.preference.PreferenceManager;
+import android.support.annotation.WorkerThread;
+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.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.DialerExecutor.Worker;
+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, and returns true if
+ * the annotated call log was updated.
+ */
+public class RefreshAnnotatedCallLogWorker implements Worker<Boolean, Boolean> {
+
+ private final Context appContext;
+ private final DataSources dataSources;
+
+ @Inject
+ public RefreshAnnotatedCallLogWorker(Context appContext, DataSources dataSources) {
+ this.appContext = appContext;
+ this.dataSources = dataSources;
+ }
+
+ @Override
+ public Boolean doInBackground(Boolean skipDirtyCheck) {
+ LogUtil.enterBlock("RefreshAnnotatedCallLogWorker.doInBackgroundFallible");
+
+ long startTime = System.currentTimeMillis();
+ boolean annotatedCallLogUpdated = checkDirtyAndRebuildIfNecessary(appContext, skipDirtyCheck);
+ LogUtil.i(
+ "RefreshAnnotatedCallLogWorker.doInBackgroundFallible",
+ "updated? %s, took %dms",
+ annotatedCallLogUpdated,
+ System.currentTimeMillis() - startTime);
+ return annotatedCallLogUpdated;
+ }
+
+ @WorkerThread
+ private boolean checkDirtyAndRebuildIfNecessary(Context appContext, boolean skipDirtyCheck) {
+ Assert.isWorkerThread();
+
+ long startTime = System.currentTimeMillis();
+
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext);
+ 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, false);
+ if (forceRebuildPrefValue) {
+ LogUtil.i(
+ "RefreshAnnotatedCallLogWorker.checkDirtyAndRebuildIfNecessary",
+ "call log has been marked dirty");
+ }
+
+ 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, 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
+ private boolean isDirty(Context appContext) {
+ Assert.isWorkerThread();
+
+ for (CallLogDataSource dataSource : dataSources.getDataSourcesIncludingSystemCallLog()) {
+ String dataSourceName = getName(dataSource);
+ long startTime = System.currentTimeMillis();
+ LogUtil.i("RefreshAnnotatedCallLogWorker.isDirty", "running isDirty for %s", dataSourceName);
+ boolean isDirty = dataSource.isDirty(appContext);
+ LogUtil.i(
+ "RefreshAnnotatedCallLogWorker.isDirty",
+ "%s.isDirty returned %b in %dms",
+ dataSourceName,
+ isDirty,
+ System.currentTimeMillis() - startTime);
+ if (isDirty) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @TargetApi(Build.VERSION_CODES.M) // Uses try-with-resources
+ @WorkerThread
+ private void rebuild(Context appContext, long lastRebuildTimeMillis) {
+ Assert.isWorkerThread();
+
+ // TODO: Start a transaction?
+ try (SQLiteDatabase database = AnnotatedCallLog.getWritableDatabase(appContext)) {
+
+ CallLogMutations mutations = new CallLogMutations();
+
+ // 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, database, lastRebuildTimeMillis, mutations);
+ LogUtil.i(
+ "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);
+ }
+
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext);
+ sharedPreferences
+ .edit()
+ .putBoolean(CallLogFramework.PREF_FORCE_REBUILD, false)
+ .putLong(CallLogFramework.PREF_LAST_REBUILD_TIMESTAMP_MILLIS, System.currentTimeMillis())
+ .commit();
+ }
+
+ private static String getName(CallLogDataSource dataSource) {
+ return dataSource.getClass().getSimpleName();
+ }
+}
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/AnnotatedCallLogDatabaseHelper.java b/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java
new file mode 100644
index 000000000..7b28e5505
--- /dev/null
+++ b/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.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 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.common.LogUtil;
+
+/** {@link SQLiteOpenHelper} for the AnnotatedCallLog database. */
+class AnnotatedCallLogDatabaseHelper extends SQLiteOpenHelper {
+
+ 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_NAME + " (")
+ .append(ID + " integer primary key, ")
+ .append(TIMESTAMP + " integer, ")
+ .append(CONTACT_NAME + " string")
+ .append(");")
+ .toString();
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ LogUtil.enterBlock("AnnotatedCallLogDatabaseHelper.onCreate");
+ long startTime = System.currentTimeMillis();
+ db.execSQL(CREATE_SQL);
+ // TODO: Consider logging impression.
+ LogUtil.i(
+ "AnnotatedCallLogDatabaseHelper.onCreate",
+ "took: %dms",
+ System.currentTimeMillis() - startTime);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
+}
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/datasources/CallLogDataSource.java b/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
new file mode 100644
index 000000000..13d0b842d
--- /dev/null
+++ b/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
@@ -0,0 +1,68 @@
+/*
+ * 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.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.support.annotation.MainThread;
+import android.support.annotation.WorkerThread;
+import com.android.dialer.calllog.database.CallLogMutations;
+
+/** A source of data for one or more columns in the annotated call log. */
+public interface CallLogDataSource {
+
+ /**
+ * A lightweight check which runs frequently to detect if the annotated call log is out of date
+ * with respect to this data source.
+ *
+ * <p>This is typically used to detect external changes to the underlying data source which have
+ * been made in such a way that the dialer application was not notified.
+ *
+ * <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.
+ */
+ @WorkerThread
+ boolean isDirty(Context appContext);
+
+ /**
+ * Computes the set of mutations necessary to update the annotated call log with respect to this
+ * data source.
+ *
+ * @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,
+ SQLiteDatabase readableDatabase,
+ long lastRebuildTimeMillis,
+ CallLogMutations mutations);
+
+ @MainThread
+ void registerContentObservers(
+ Context appContext, ContentObserverCallbacks contentObserverCallbacks);
+
+ /**
+ * Methods which may optionally be called as a result of a data source's content observer firing.
+ */
+ interface ContentObserverCallbacks {
+ @MainThread
+ void markDirtyAndNotify(Context appContext);
+ }
+}
diff --git a/java/com/android/dialer/calllog/datasources/contacts/ContactsDataSource.java b/java/com/android/dialer/calllog/datasources/contacts/ContactsDataSource.java
new file mode 100644
index 000000000..241be5d71
--- /dev/null
+++ b/java/com/android/dialer/calllog/datasources/contacts/ContactsDataSource.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.datasources.contacts;
+
+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.CallLogMutations;
+import com.android.dialer.calllog.datasources.CallLogDataSource;
+import com.android.dialer.common.Assert;
+import javax.inject.Inject;
+
+/** Responsible for maintaining the contacts related columns in the annotated call log. */
+public final class ContactsDataSource implements CallLogDataSource {
+
+ @Inject
+ public ContactsDataSource() {}
+
+ @WorkerThread
+ @Override
+ public boolean isDirty(Context appContext) {
+ Assert.isWorkerThread();
+
+ // TODO: Implementation.
+ return false;
+ }
+
+ @WorkerThread
+ @Override
+ public void fill(
+ Context appContext,
+ SQLiteDatabase readableDatabase,
+ long lastRebuildTimeMillis,
+ CallLogMutations mutations) {
+ Assert.isWorkerThread();
+ // TODO: Implementation.
+ }
+
+ @MainThread
+ @Override
+ public void registerContentObservers(
+ Context appContext, ContentObserverCallbacks contentObserverCallbacks) {}
+}
diff --git a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
new file mode 100644
index 000000000..1cc51ee99
--- /dev/null
+++ b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
@@ -0,0 +1,114 @@
+/*
+ * 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.systemcalllog;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.CallLog;
+import android.support.annotation.MainThread;
+import android.support.annotation.WorkerThread;
+import com.android.dialer.calllog.database.CallLogMutations;
+import com.android.dialer.calllog.datasources.CallLogDataSource;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.ThreadUtil;
+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.
+ */
+public class SystemCallLogDataSource implements CallLogDataSource {
+
+ @Inject
+ public SystemCallLogDataSource() {}
+
+ @MainThread
+ @Override
+ public void registerContentObservers(
+ Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ Assert.isMainThread();
+
+ appContext
+ .getContentResolver()
+ .registerContentObserver(
+ CallLog.Calls.CONTENT_URI,
+ true,
+ new CallLogObserver(
+ ThreadUtil.getUiThreadHandler(), appContext, contentObserverCallbacks));
+ }
+
+ @WorkerThread
+ @Override
+ public boolean isDirty(Context appContext) {
+ Assert.isWorkerThread();
+
+ /*
+ * The system call log has a last updated timestamp, but deletes are physical (the "deleted"
+ * column is unused). This means that we can't detect deletes without scanning the entire table,
+ * which would be too slow. So, we just rely on content observers to trigger rebuilds when any
+ * change is made to the system call log.
+ */
+ return false;
+ }
+
+ @WorkerThread
+ @Override
+ public void fill(
+ Context appContext,
+ SQLiteDatabase readableDatabase,
+ long lastRebuildTimeMillis,
+ CallLogMutations mutations) {
+ Assert.isWorkerThread();
+
+ // This data source should always run first so the mutations should always be empty.
+ Assert.checkState(mutations.isEmpty());
+
+ // TODO: Implementation.
+ }
+
+ private static class CallLogObserver extends ContentObserver {
+ private final Context appContext;
+ private final ContentObserverCallbacks contentObserverCallbacks;
+
+ CallLogObserver(
+ Handler handler, Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ super(handler);
+ this.appContext = appContext;
+ this.contentObserverCallbacks = contentObserverCallbacks;
+ }
+
+ @MainThread
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ Assert.isMainThread();
+ LogUtil.enterBlock("SystemCallLogDataSource.CallLogObserver.onChange");
+ super.onChange(selfChange, uri);
+
+ /*
+ * The system call log has a last updated timestamp, but deletes are physical (the "deleted"
+ * column is unused). This means that we can't detect deletes without scanning the entire
+ * table, which would be too slow. So, we just rely on content observers to trigger rebuilds
+ * when any change is made to the system call log.
+ */
+ contentObserverCallbacks.markDirtyAndNotify(appContext);
+ }
+ }
+}
diff --git a/java/com/android/dialer/calllog/ui/AndroidManifest.xml b/java/com/android/dialer/calllog/ui/AndroidManifest.xml
new file mode 100644
index 000000000..228167749
--- /dev/null
+++ b/java/com/android/dialer/calllog/ui/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<!--
+ ~ 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 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/NewCallLogFragment.java b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
new file mode 100644
index 000000000..b8f2b1326
--- /dev/null
+++ b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
@@ -0,0 +1,138 @@
+/*
+ * 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.app.Fragment;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Context;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+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.AnnotatedCallLog.Columns;
+import com.android.dialer.common.LogUtil;
+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 CursorAdapter cursorAdapter;
+
+ public NewCallLogFragment() {
+ LogUtil.enterBlock("NewCallLogFragment.NewCallLogFragment");
+ }
+
+ @Override
+ public void onCreate(Bundle state) {
+ super.onCreate(state);
+
+ LogUtil.enterBlock("NewCallLogFragment.onCreate");
+
+ CallLogFramework callLogFramework = CallLogComponent.get(getContext()).callLogFramework();
+ callLogFramework.attachUi(this);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ LogUtil.enterBlock("NewCallLogFragment.onResume");
+
+ CallLogFramework callLogFramework = CallLogComponent.get(getContext()).callLogFramework();
+ callLogFramework.attachUi(this);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ LogUtil.enterBlock("NewCallLogFragment.onPause");
+
+ CallLogFramework callLogFramework = CallLogComponent.get(getContext()).callLogFramework();
+ callLogFramework.detachUi();
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ LogUtil.enterBlock("NewCallLogFragment.onCreateView");
+
+ View view = inflater.inflate(R.layout.new_call_log_fragment, container, false);
+ ListView listView = (ListView) view.findViewById(R.id.list);
+
+ 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);
+
+ getLoaderManager().initLoader(0, null, this);
+
+ return view;
+ }
+
+ @Override
+ public void invalidateUi() {
+ LogUtil.enterBlock("NewCallLogFragment.invalidateUi");
+ // TODO: Implementation.
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ // 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) {
+ cursorAdapter.swapCursor(newCursor);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ 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/res/layout/new_call_log_entry.xml b/java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml
new file mode 100644
index 000000000..ee3efd002
--- /dev/null
+++ b/java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/timestamp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/contact_name"
+ android:layout_width="wrap_content"
+ 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
new file mode 100644
index 000000000..433dbdd0f
--- /dev/null
+++ b/java/com/android/dialer/calllog/ui/res/layout/new_call_log_fragment.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<ListView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>