summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2018-04-09 23:19:31 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2018-04-09 23:19:31 +0000
commita5cefeb5cf93f2e6256eced1302f4187f5450c93 (patch)
tree4a65b25503a7a64ae7778e70167f7f3b394f29c9 /java
parentff404929afbea0bacce10512b8a5adc9ab28e509 (diff)
parent178b452d6ade018ebb7f2209ccc8233d4fa8acaa (diff)
Merge changes I7a22367b,Ifa35451c,I70f3abbe
* changes: Autogenerate IDs for inserts and consolidate loader into one transaction. Ensure listeners are added each time setUp is called Implemented SpeedDialUiItemLoader.
Diffstat (limited to 'java')
-rw-r--r--java/com/android/dialer/speeddial/SpeedDialUiItem.java159
-rw-r--r--java/com/android/dialer/speeddial/SpeedDialUiItemLoader.java216
-rw-r--r--java/com/android/dialer/speeddial/database/SpeedDialEntry.java21
-rw-r--r--java/com/android/dialer/speeddial/database/SpeedDialEntryDao.java32
-rw-r--r--java/com/android/dialer/speeddial/database/SpeedDialEntryDatabaseHelper.java157
-rw-r--r--java/com/android/incallui/InCallPresenter.java16
6 files changed, 532 insertions, 69 deletions
diff --git a/java/com/android/dialer/speeddial/SpeedDialUiItem.java b/java/com/android/dialer/speeddial/SpeedDialUiItem.java
new file mode 100644
index 000000000..17552adf7
--- /dev/null
+++ b/java/com/android/dialer/speeddial/SpeedDialUiItem.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.dialer.speeddial;
+
+import android.database.Cursor;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import com.android.dialer.common.Assert;
+import com.android.dialer.speeddial.database.SpeedDialEntry;
+import com.android.dialer.speeddial.database.SpeedDialEntry.Channel;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * POJO representation of each speed dial list element.
+ *
+ * <p>Contains all data needed for the UI so that the UI never needs do additional contact queries.
+ *
+ * <p>Differs from {@link SpeedDialEntry} in that entries are specific to favorited/starred contacts
+ * and {@link SpeedDialUiItem}s can be both favorites and suggested contacts.
+ */
+@AutoValue
+public abstract class SpeedDialUiItem {
+
+ public static final int LOOKUP_KEY = 0;
+ public static final int CONTACT_ID = 1;
+ public static final int DISPLAY_NAME = 2;
+ public static final int STARRED = 3;
+ public static final int NUMBER = 4;
+ public static final int LABEL = 5;
+ public static final int PHOTO_ID = 6;
+ public static final int PHOTO_URI = 7;
+
+ public static final String[] PHONE_PROJECTION = {
+ Phone.LOOKUP_KEY,
+ Phone.CONTACT_ID,
+ Phone.DISPLAY_NAME,
+ Phone.STARRED,
+ Phone.NUMBER,
+ Phone.LABEL,
+ Phone.PHOTO_ID,
+ Phone.PHOTO_URI
+ };
+
+ public static Builder builder() {
+ return new AutoValue_SpeedDialUiItem.Builder().setChannels(ImmutableList.of());
+ }
+
+ /** Convert a cursor with projection {@link #PHONE_PROJECTION} into a {@link SpeedDialUiItem}. */
+ public static SpeedDialUiItem fromCursor(Cursor cursor) {
+ Assert.checkArgument(cursor != null);
+ Assert.checkArgument(cursor.getCount() != 0);
+ String lookupKey = cursor.getString(LOOKUP_KEY);
+ SpeedDialUiItem.Builder builder =
+ SpeedDialUiItem.builder()
+ .setLookupKey(lookupKey)
+ .setContactId(cursor.getLong(CONTACT_ID))
+ // TODO(a bug): handle last name first preference
+ .setName(cursor.getString(DISPLAY_NAME))
+ .setIsStarred(cursor.getInt(STARRED) == 1)
+ .setPhotoId(cursor.getLong(PHOTO_ID))
+ .setPhotoUri(
+ TextUtils.isEmpty(cursor.getString(PHOTO_URI)) ? "" : cursor.getString(PHOTO_URI));
+
+ // While there are more rows and the lookup keys are the same, add a channel for each of the
+ // contact's phone numbers.
+ List<Channel> channels = new ArrayList<>();
+ do {
+ channels.add(
+ Channel.builder()
+ .setNumber(cursor.getString(NUMBER))
+ .setLabel(TextUtils.isEmpty(cursor.getString(LABEL)) ? "" : cursor.getString(LABEL))
+ // TODO(a bug): add another channel for each technology (Duo, ViLTE, ect.)
+ .setTechnology(Channel.VOICE)
+ .build());
+ } while (cursor.moveToNext() && Objects.equals(lookupKey, cursor.getString(LOOKUP_KEY)));
+
+ builder.setChannels(ImmutableList.copyOf(channels));
+ return builder.build();
+ }
+
+ /** @see android.provider.ContactsContract.Contacts#DISPLAY_NAME */
+ public abstract String name();
+
+ /** @see android.provider.ContactsContract.Contacts#_ID */
+ public abstract long contactId();
+
+ /** @see android.provider.ContactsContract.Contacts#LOOKUP_KEY */
+ public abstract String lookupKey();
+
+ /** @see android.provider.ContactsContract.Contacts#STARRED */
+ public abstract boolean isStarred();
+
+ /** @see Phone#PHOTO_ID */
+ public abstract long photoId();
+
+ /** @see Phone#PHOTO_URI */
+ public abstract String photoUri();
+
+ /**
+ * Since a contact can have multiple phone numbers and each number can have multiple technologies,
+ * enumerate each one here so that the user can choose the correct one. Each channel here
+ * represents a row in the {@link com.android.dialer.speeddial.DisambigDialog}.
+ *
+ * @see com.android.dialer.speeddial.database.SpeedDialEntry.Channel
+ */
+ public abstract ImmutableList<Channel> channels();
+
+ /**
+ * Will be null when the user hasn't chosen a default yet.
+ *
+ * @see com.android.dialer.speeddial.database.SpeedDialEntry#defaultChannel()
+ */
+ public abstract @Nullable Channel defaultChannel();
+
+ public abstract Builder toBuilder();
+
+ /** Builder class for speed dial contact. */
+ @AutoValue.Builder
+ public abstract static class Builder {
+
+ public abstract Builder setName(String name);
+
+ public abstract Builder setContactId(long contactId);
+
+ public abstract Builder setLookupKey(String lookupKey);
+
+ public abstract Builder setIsStarred(boolean isStarred);
+
+ public abstract Builder setPhotoId(long photoId);
+
+ public abstract Builder setPhotoUri(String photoUri);
+
+ public abstract Builder setChannels(ImmutableList<Channel> channels);
+
+ /** Set to null if the user hasn't chosen a default or the channel no longer exists. */
+ public abstract Builder setDefaultChannel(@Nullable Channel defaultChannel);
+
+ public abstract SpeedDialUiItem build();
+ }
+}
diff --git a/java/com/android/dialer/speeddial/SpeedDialUiItemLoader.java b/java/com/android/dialer/speeddial/SpeedDialUiItemLoader.java
new file mode 100644
index 000000000..13e5f8744
--- /dev/null
+++ b/java/com/android/dialer/speeddial/SpeedDialUiItemLoader.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.dialer.speeddial;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.support.annotation.WorkerThread;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
+import com.android.dialer.common.concurrent.DialerExecutor.SuccessListener;
+import com.android.dialer.inject.ApplicationContext;
+import com.android.dialer.speeddial.database.SpeedDialEntry;
+import com.android.dialer.speeddial.database.SpeedDialEntry.Channel;
+import com.android.dialer.speeddial.database.SpeedDialEntryDao;
+import com.android.dialer.speeddial.database.SpeedDialEntryDatabaseHelper;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import java.util.ArrayList;
+import java.util.List;
+import javax.inject.Inject;
+
+/**
+ * Loads a list of {@link SpeedDialUiItem SpeedDialUiItems}.
+ *
+ * @see #loadSpeedDialUiItems()
+ * <ol>
+ * <li>Retrieve the list of {@link SpeedDialEntry} from {@link SpeedDialEntryDatabaseHelper}.
+ * <li>Build a list of {@link SpeedDialUiItem} based on {@link SpeedDialEntry#lookupKey()} in
+ * {@link Phone#CONTENT_URI}.
+ * <li>Remove any {@link SpeedDialEntry} that is no longer starred or whose contact was
+ * deleted.
+ * <li>Update each {@link SpeedDialEntry} contact id, lookup key and channel.
+ * <li>Build a list of {@link SpeedDialUiItem} from {@link Contacts#STREQUENT_PHONE_ONLY}.
+ * <li>If any starred contacts in that list aren't in the {@link
+ * SpeedDialEntryDatabaseHelper}, insert them now.
+ * <li>Notify the {@link SuccessListener} of the complete list of {@link SpeedDialUiItem
+ * SpeedDialContacts} composed from {@link SpeedDialEntry SpeedDialEntries} and
+ * non-starred {@link Contacts#STREQUENT_PHONE_ONLY}.
+ * </ol>
+ */
+@SuppressWarnings("AndroidApiChecker")
+@TargetApi(VERSION_CODES.N)
+public final class SpeedDialUiItemLoader {
+
+ private final Context appContext;
+ private final ListeningExecutorService backgroundExecutor;
+
+ @Inject
+ public SpeedDialUiItemLoader(
+ @ApplicationContext Context appContext,
+ @BackgroundExecutor ListeningExecutorService backgroundExecutor) {
+ this.appContext = appContext;
+ this.backgroundExecutor = backgroundExecutor;
+ }
+
+ /**
+ * Returns a {@link ListenableFuture} for a list of {@link SpeedDialUiItem SpeedDialUiItems}. This
+ * list is composed of starred contacts from {@link SpeedDialEntryDatabaseHelper} and suggestions
+ * from {@link Contacts#STREQUENT_PHONE_ONLY}.
+ */
+ public ListenableFuture<ImmutableList<SpeedDialUiItem>> loadSpeedDialUiItems() {
+ return backgroundExecutor.submit(this::doInBackground);
+ }
+
+ @WorkerThread
+ private ImmutableList<SpeedDialUiItem> doInBackground() {
+ Assert.isWorkerThread();
+ SpeedDialEntryDao db = new SpeedDialEntryDatabaseHelper(appContext);
+
+ // This is the list of contacts that we will display to the user
+ List<SpeedDialUiItem> speedDialUiItems = new ArrayList<>();
+
+ // We'll use these lists to update the SpeedDialEntry database
+ List<SpeedDialEntry> entriesToInsert = new ArrayList<>();
+ List<SpeedDialEntry> entriesToUpdate = new ArrayList<>();
+ List<Long> entriesToDelete = new ArrayList<>();
+
+ // Get all SpeedDialEntries and mark them to be updated or deleted
+ List<SpeedDialEntry> entries = db.getAllEntries();
+ for (SpeedDialEntry entry : entries) {
+ SpeedDialUiItem contact = getSpeedDialContact(entry);
+ // Remove contacts that no longer exist or are no longer starred
+ if (contact == null || !contact.isStarred()) {
+ entriesToDelete.add(entry.id());
+ continue;
+ }
+
+ // Contact exists, so update its entry in SpeedDialEntry Database
+ entriesToUpdate.add(
+ entry
+ .toBuilder()
+ .setLookupKey(contact.lookupKey())
+ .setContactId(contact.contactId())
+ .setDefaultChannel(contact.defaultChannel())
+ .build());
+
+ // These are our existing starred entries
+ speedDialUiItems.add(contact);
+ }
+
+ // Get all Strequent Contacts
+ List<SpeedDialUiItem> strequentContacts = getStrequentContacts();
+
+ // For each contact, if it isn't starred, add it as a suggestion.
+ // If it is starred and not already accounted for above, then insert into the SpeedDialEntry DB.
+ for (SpeedDialUiItem contact : strequentContacts) {
+ if (!contact.isStarred()) {
+ // Add this contact as a suggestion
+ // TODO(calderwoodra): set the defaults of these automatically
+ speedDialUiItems.add(contact);
+
+ } else if (speedDialUiItems.stream().noneMatch(c -> c.contactId() == contact.contactId())) {
+ entriesToInsert.add(
+ SpeedDialEntry.builder()
+ .setLookupKey(contact.lookupKey())
+ .setContactId(contact.contactId())
+ .setDefaultChannel(contact.defaultChannel())
+ .build());
+
+ // These are our newly starred contacts
+ speedDialUiItems.add(contact);
+ }
+ }
+
+ db.insertUpdateAndDelete(
+ ImmutableList.copyOf(entriesToInsert),
+ ImmutableList.copyOf(entriesToUpdate),
+ ImmutableList.copyOf(entriesToDelete));
+ return ImmutableList.copyOf(speedDialUiItems);
+ }
+
+ @WorkerThread
+ private SpeedDialUiItem getSpeedDialContact(SpeedDialEntry entry) {
+ Assert.isWorkerThread();
+ // TODO(b77725860): Might need to use the lookup uri to get the contact id first, then query
+ // based on that.
+ SpeedDialUiItem contact;
+ try (Cursor cursor =
+ appContext
+ .getContentResolver()
+ .query(
+ Phone.CONTENT_URI,
+ SpeedDialUiItem.PHONE_PROJECTION,
+ Phone.NUMBER + " IS NOT NULL AND " + Phone.LOOKUP_KEY + "=?",
+ new String[] {entry.lookupKey()},
+ null)) {
+
+ if (cursor == null || cursor.getCount() == 0) {
+ // Contact not found, potentially deleted
+ LogUtil.e("SpeedDialUiItemLoader.getSpeedDialContact", "Contact not found.");
+ return null;
+ }
+
+ cursor.moveToFirst();
+ contact = SpeedDialUiItem.fromCursor(cursor);
+ }
+
+ // Preserve the default channel if it didn't change/still exists
+ Channel defaultChannel = entry.defaultChannel();
+ if (defaultChannel != null) {
+ if (contact.channels().contains(defaultChannel)) {
+ contact = contact.toBuilder().setDefaultChannel(defaultChannel).build();
+ }
+ }
+
+ // TODO(calderwoodra): Consider setting the default channel if there is only one channel
+ return contact;
+ }
+
+ @WorkerThread
+ private List<SpeedDialUiItem> getStrequentContacts() {
+ Assert.isWorkerThread();
+ Uri uri =
+ Contacts.CONTENT_STREQUENT_URI
+ .buildUpon()
+ .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true")
+ .build();
+ try (Cursor cursor =
+ appContext
+ .getContentResolver()
+ .query(uri, SpeedDialUiItem.PHONE_PROJECTION, null, null, null)) {
+ List<SpeedDialUiItem> contacts = new ArrayList<>();
+ if (cursor == null || cursor.getCount() == 0) {
+ return contacts;
+ }
+
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ contacts.add(SpeedDialUiItem.fromCursor(cursor));
+ }
+ return contacts;
+ }
+ }
+}
diff --git a/java/com/android/dialer/speeddial/database/SpeedDialEntry.java b/java/com/android/dialer/speeddial/database/SpeedDialEntry.java
index aa90909f1..5b54b79c8 100644
--- a/java/com/android/dialer/speeddial/database/SpeedDialEntry.java
+++ b/java/com/android/dialer/speeddial/database/SpeedDialEntry.java
@@ -27,8 +27,13 @@ import java.lang.annotation.RetentionPolicy;
@AutoValue
public abstract class SpeedDialEntry {
- /** Unique ID */
- public abstract long id();
+ /**
+ * Unique ID
+ *
+ * <p>Must be null when inserting, and an ID will be generated and returned after inserting.
+ */
+ @Nullable
+ public abstract Long id();
/** @see {@link Contacts#_ID} */
public abstract long contactId();
@@ -55,7 +60,7 @@ public abstract class SpeedDialEntry {
@AutoValue.Builder
public abstract static class Builder {
- public abstract Builder setId(long id);
+ public abstract Builder setId(Long id);
public abstract Builder setContactId(long contactId);
@@ -72,13 +77,17 @@ public abstract class SpeedDialEntry {
public static final int UNKNOWN = 0;
public static final int VOICE = 1;
- public static final int VIDEO = 2;
+ public static final int IMS_VIDEO = 2;
+ public static final int DUO = 3;
/** Whether the Channel is for an audio or video call. */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({UNKNOWN, VOICE, VIDEO})
+ @IntDef({UNKNOWN, VOICE, IMS_VIDEO, DUO})
public @interface Technology {}
+ public boolean isVideoTechnology() {
+ return technology() == IMS_VIDEO || technology() == DUO;
+ }
/**
* Raw phone number as the user entered it.
*
@@ -96,6 +105,8 @@ public abstract class SpeedDialEntry {
public abstract @Technology int technology();
+ public abstract Builder toBuilder();
+
public static Builder builder() {
return new AutoValue_SpeedDialEntry_Channel.Builder();
}
diff --git a/java/com/android/dialer/speeddial/database/SpeedDialEntryDao.java b/java/com/android/dialer/speeddial/database/SpeedDialEntryDao.java
index 39cb115c8..ce771c3c8 100644
--- a/java/com/android/dialer/speeddial/database/SpeedDialEntryDao.java
+++ b/java/com/android/dialer/speeddial/database/SpeedDialEntryDao.java
@@ -16,25 +16,29 @@
package com.android.dialer.speeddial.database;
-import java.util.List;
+import com.google.common.collect.ImmutableList;
-/** Interface that databases support speed dial entries should implement. */
+/**
+ * Interface that databases support speed dial entries should implement.
+ *
+ * <p>This database is only used for favorite/starred contacts.
+ */
public interface SpeedDialEntryDao {
/** Return all entries in the database */
- List<SpeedDialEntry> getAllEntries();
+ ImmutableList<SpeedDialEntry> getAllEntries();
/**
* Insert new entries.
*
- * <p>Fails if any of the {@link SpeedDialEntry#id()} already exist.
+ * <p>{@link SpeedDialEntry#id() ids} must be null.
*/
- void insert(List<SpeedDialEntry> entries);
+ void insert(ImmutableList<SpeedDialEntry> entries);
/**
* Insert a new entry.
*
- * <p>Fails if the {@link SpeedDialEntry#id()} already exists.
+ * <p>{@link SpeedDialEntry#id() ids} must be null.
*/
long insert(SpeedDialEntry entry);
@@ -43,14 +47,26 @@ public interface SpeedDialEntryDao {
*
* <p>Fails if the {@link SpeedDialEntry#id()} doesn't exist.
*/
- void update(List<SpeedDialEntry> entries);
+ void update(ImmutableList<SpeedDialEntry> entries);
/**
* Delete the passed in entries based on {@link SpeedDialEntry#id}.
*
* <p>Fails if the {@link SpeedDialEntry#id()} doesn't exist.
*/
- void delete(List<Long> entries);
+ void delete(ImmutableList<Long> entries);
+
+ /**
+ * Inserts, updates and deletes rows all in on transaction.
+ *
+ * @see #insert(ImmutableList)
+ * @see #update(ImmutableList)
+ * @see #delete(ImmutableList)
+ */
+ void insertUpdateAndDelete(
+ ImmutableList<SpeedDialEntry> entriesToInsert,
+ ImmutableList<SpeedDialEntry> entriesToUpdate,
+ ImmutableList<Long> entriesToDelete);
/** Delete all entries in the database. */
void deleteAll();
diff --git a/java/com/android/dialer/speeddial/database/SpeedDialEntryDatabaseHelper.java b/java/com/android/dialer/speeddial/database/SpeedDialEntryDatabaseHelper.java
index 1812dbdc0..7c823bd63 100644
--- a/java/com/android/dialer/speeddial/database/SpeedDialEntryDatabaseHelper.java
+++ b/java/com/android/dialer/speeddial/database/SpeedDialEntryDatabaseHelper.java
@@ -25,10 +25,15 @@ import android.text.TextUtils;
import com.android.dialer.common.Assert;
import com.android.dialer.common.database.Selection;
import com.android.dialer.speeddial.database.SpeedDialEntry.Channel;
+import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
-/** {@link SpeedDialEntryDao} implemented as an SQLite database. */
+/**
+ * {@link SpeedDialEntryDao} implemented as an SQLite database.
+ *
+ * @see SpeedDialEntryDao
+ */
public final class SpeedDialEntryDatabaseHelper extends SQLiteOpenHelper
implements SpeedDialEntryDao {
@@ -42,7 +47,7 @@ public final class SpeedDialEntryDatabaseHelper extends SQLiteOpenHelper
private static final String LOOKUP_KEY = "lookup_key";
private static final String PHONE_NUMBER = "phone_number";
private static final String PHONE_LABEL = "phone_label";
- private static final String PHONE_TYPE = "phone_type";
+ private static final String PHONE_TECHNOLOGY = "phone_technology";
// Column positions
private static final int POSITION_ID = 0;
@@ -50,7 +55,7 @@ public final class SpeedDialEntryDatabaseHelper extends SQLiteOpenHelper
private static final int POSITION_LOOKUP_KEY = 2;
private static final int POSITION_PHONE_NUMBER = 3;
private static final int POSITION_PHONE_LABEL = 4;
- private static final int POSITION_PHONE_TYPE = 5;
+ private static final int POSITION_PHONE_TECHNOLOGY = 5;
// Create Table Query
private static final String CREATE_TABLE_SQL =
@@ -62,7 +67,7 @@ public final class SpeedDialEntryDatabaseHelper extends SQLiteOpenHelper
+ (LOOKUP_KEY + " text, ")
+ (PHONE_NUMBER + " text, ")
+ (PHONE_LABEL + " text, ")
- + (PHONE_TYPE + " integer ")
+ + (PHONE_TECHNOLOGY + " integer ")
+ ");";
private static final String DELETE_TABLE_SQL = "drop table if exists " + TABLE_NAME;
@@ -90,7 +95,7 @@ public final class SpeedDialEntryDatabaseHelper extends SQLiteOpenHelper
}
@Override
- public List<SpeedDialEntry> getAllEntries() {
+ public ImmutableList<SpeedDialEntry> getAllEntries() {
List<SpeedDialEntry> entries = new ArrayList<>();
String query = "SELECT * FROM " + TABLE_NAME;
@@ -98,39 +103,40 @@ public final class SpeedDialEntryDatabaseHelper extends SQLiteOpenHelper
Cursor cursor = db.rawQuery(query, null)) {
cursor.moveToPosition(-1);
while (cursor.moveToNext()) {
- Channel channel =
- Channel.builder()
- .setNumber(cursor.getString(POSITION_PHONE_NUMBER))
- .setLabel(cursor.getString(POSITION_PHONE_LABEL))
- .setTechnology(cursor.getInt(POSITION_PHONE_TYPE))
- .build();
- if (TextUtils.isEmpty(channel.number())) {
- channel = null;
+ String number = cursor.getString(POSITION_PHONE_NUMBER);
+ Channel channel = null;
+ if (!TextUtils.isEmpty(number)) {
+ channel =
+ Channel.builder()
+ .setNumber(number)
+ .setLabel(cursor.getString(POSITION_PHONE_LABEL))
+ .setTechnology(cursor.getInt(POSITION_PHONE_TECHNOLOGY))
+ .build();
}
+
SpeedDialEntry entry =
SpeedDialEntry.builder()
.setDefaultChannel(channel)
.setContactId(cursor.getLong(POSITION_CONTACT_ID))
.setLookupKey(cursor.getString(POSITION_LOOKUP_KEY))
- .setId(cursor.getInt(POSITION_ID))
+ .setId(cursor.getLong(POSITION_ID))
.build();
entries.add(entry);
}
}
- return entries;
+ return ImmutableList.copyOf(entries);
}
@Override
- public void insert(List<SpeedDialEntry> entries) {
+ public void insert(ImmutableList<SpeedDialEntry> entries) {
+ if (entries.isEmpty()) {
+ return;
+ }
+
SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
try {
- for (SpeedDialEntry entry : entries) {
- if (db.insert(TABLE_NAME, null, buildContentValues(entry)) == -1L) {
- throw Assert.createUnsupportedOperationFailException(
- "Attempted to insert a row that already exists.");
- }
- }
+ insert(db, entries);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
@@ -138,11 +144,21 @@ public final class SpeedDialEntryDatabaseHelper extends SQLiteOpenHelper
}
}
+ private void insert(SQLiteDatabase writeableDatabase, ImmutableList<SpeedDialEntry> entries) {
+ for (SpeedDialEntry entry : entries) {
+ Assert.checkArgument(entry.id() == null);
+ if (writeableDatabase.insert(TABLE_NAME, null, buildContentValuesWithoutId(entry)) == -1L) {
+ throw Assert.createUnsupportedOperationFailException(
+ "Attempted to insert a row that already exists.");
+ }
+ }
+ }
+
@Override
public long insert(SpeedDialEntry entry) {
long updateRowId;
try (SQLiteDatabase db = getWritableDatabase()) {
- updateRowId = db.insert(TABLE_NAME, null, buildContentValues(entry));
+ updateRowId = db.insert(TABLE_NAME, null, buildContentValuesWithoutId(entry));
}
if (updateRowId == -1) {
throw Assert.createUnsupportedOperationFailException(
@@ -152,22 +168,15 @@ public final class SpeedDialEntryDatabaseHelper extends SQLiteOpenHelper
}
@Override
- public void update(List<SpeedDialEntry> entries) {
+ public void update(ImmutableList<SpeedDialEntry> entries) {
+ if (entries.isEmpty()) {
+ return;
+ }
+
SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
try {
- for (SpeedDialEntry entry : entries) {
- int count =
- db.update(
- TABLE_NAME,
- buildContentValues(entry),
- ID + " = ?",
- new String[] {Long.toString(entry.id())});
- if (count != 1) {
- throw Assert.createUnsupportedOperationFailException(
- "Attempted to update an undetermined number of rows: " + count);
- }
- }
+ update(db, entries);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
@@ -175,33 +184,89 @@ public final class SpeedDialEntryDatabaseHelper extends SQLiteOpenHelper
}
}
- private ContentValues buildContentValues(SpeedDialEntry entry) {
+ private void update(SQLiteDatabase writeableDatabase, ImmutableList<SpeedDialEntry> entries) {
+ for (SpeedDialEntry entry : entries) {
+ int count =
+ writeableDatabase.update(
+ TABLE_NAME,
+ buildContentValuesWithId(entry),
+ ID + " = ?",
+ new String[] {Long.toString(entry.id())});
+ if (count != 1) {
+ throw Assert.createUnsupportedOperationFailException(
+ "Attempted to update an undetermined number of rows: " + count);
+ }
+ }
+ }
+
+ private ContentValues buildContentValuesWithId(SpeedDialEntry entry) {
+ return buildContentValues(entry, true);
+ }
+
+ private ContentValues buildContentValuesWithoutId(SpeedDialEntry entry) {
+ return buildContentValues(entry, false);
+ }
+
+ private ContentValues buildContentValues(SpeedDialEntry entry, boolean includeId) {
ContentValues values = new ContentValues();
- values.put(ID, entry.id());
+ if (includeId) {
+ values.put(ID, entry.id());
+ }
values.put(CONTACT_ID, entry.contactId());
values.put(LOOKUP_KEY, entry.lookupKey());
if (entry.defaultChannel() != null) {
values.put(PHONE_NUMBER, entry.defaultChannel().number());
values.put(PHONE_LABEL, entry.defaultChannel().label());
- values.put(PHONE_TYPE, entry.defaultChannel().technology());
+ values.put(PHONE_TECHNOLOGY, entry.defaultChannel().technology());
}
return values;
}
@Override
- public void delete(List<Long> ids) {
+ public void delete(ImmutableList<Long> ids) {
+ if (ids.isEmpty()) {
+ return;
+ }
+
+ try (SQLiteDatabase db = getWritableDatabase()) {
+ delete(db, ids);
+ }
+ }
+
+ private void delete(SQLiteDatabase writeableDatabase, ImmutableList<Long> ids) {
List<String> idStrings = new ArrayList<>();
for (Long id : ids) {
idStrings.add(Long.toString(id));
}
Selection selection = Selection.builder().and(Selection.column(ID).in(idStrings)).build();
- try (SQLiteDatabase db = getWritableDatabase()) {
- int count = db.delete(TABLE_NAME, selection.getSelection(), selection.getSelectionArgs());
- if (count != ids.size()) {
- throw Assert.createUnsupportedOperationFailException(
- "Attempted to delete an undetermined number of rows: " + count);
- }
+ int count =
+ writeableDatabase.delete(
+ TABLE_NAME, selection.getSelection(), selection.getSelectionArgs());
+ if (count != ids.size()) {
+ throw Assert.createUnsupportedOperationFailException(
+ "Attempted to delete an undetermined number of rows: " + count);
+ }
+ }
+
+ @Override
+ public void insertUpdateAndDelete(
+ ImmutableList<SpeedDialEntry> entriesToInsert,
+ ImmutableList<SpeedDialEntry> entriesToUpdate,
+ ImmutableList<Long> entriesToDelete) {
+ if (entriesToInsert.isEmpty() && entriesToUpdate.isEmpty() && entriesToDelete.isEmpty()) {
+ return;
+ }
+ SQLiteDatabase db = getWritableDatabase();
+ db.beginTransaction();
+ try {
+ insert(db, entriesToInsert);
+ update(db, entriesToUpdate);
+ delete(db, entriesToDelete);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ db.close();
}
}
diff --git a/java/com/android/incallui/InCallPresenter.java b/java/com/android/incallui/InCallPresenter.java
index 7ea2fadf3..7468b83ac 100644
--- a/java/com/android/incallui/InCallPresenter.java
+++ b/java/com/android/incallui/InCallPresenter.java
@@ -266,8 +266,6 @@ public class InCallPresenter implements CallList.Listener, AudioModeProvider.Aud
private VideoSurfaceTexture localVideoSurfaceTexture;
private VideoSurfaceTexture remoteVideoSurfaceTexture;
- private MotorolaInCallUiNotifier motorolaInCallUiNotifier;
-
private SpeakEasyCallManager speakEasyCallManager;
private boolean shouldStartInBubbleMode;
@@ -398,14 +396,12 @@ public class InCallPresenter implements CallList.Listener, AudioModeProvider.Aud
AudioModeProvider.getInstance().addListener(this);
- if (motorolaInCallUiNotifier == null) {
- // Add listener to notify Telephony process when the incoming call screen is started or
- // finished. This is for hiding USSD dialog because the incoming call screen should have
- // higher precedence over this dialog.
- motorolaInCallUiNotifier = new MotorolaInCallUiNotifier(context);
- addInCallUiListener(motorolaInCallUiNotifier);
- addListener(motorolaInCallUiNotifier);
- }
+ // Add listener to notify Telephony process when the incoming call screen is started or
+ // finished. This is for hiding USSD dialog because the incoming call screen should have
+ // higher precedence over this dialog.
+ MotorolaInCallUiNotifier motorolaInCallUiNotifier = new MotorolaInCallUiNotifier(context);
+ addInCallUiListener(motorolaInCallUiNotifier);
+ addListener(motorolaInCallUiNotifier);
this.shouldStartInBubbleMode = shouldStartInBubbleMode(intent);