summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/calllog/datasources
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/dialer/calllog/datasources')
-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/DataSources.java30
-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
6 files changed, 21 insertions, 470 deletions
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/DataSources.java b/java/com/android/dialer/calllog/datasources/DataSources.java
deleted file mode 100644
index 911ca3fa3..000000000
--- a/java/com/android/dialer/calllog/datasources/DataSources.java
+++ /dev/null
@@ -1,30 +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 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 {
-
- SystemCallLogDataSource getSystemCallLogDataSource();
-
- List<CallLogDataSource> getDataSourcesIncludingSystemCallLog();
-
- List<CallLogDataSource> getDataSourcesExcludingSystemCallLog();
-}
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;
- }
-}