summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuzhou <jiayuzhou@google.com>2018-04-21 17:20:47 -0700
committerYuzhou <jiayuzhou@google.com>2018-05-01 13:30:47 -0700
commit02a83b90ad2a990c22b4654fbfa23b84aae0fafc (patch)
treeea84f616e82ffabe9c95cdd8cd5a25c987c75322
parent43d9a9e1f5a60fe13230dc072589c75bbf1d1d29 (diff)
downloadDialer-02a83b90ad2a990c22b4654fbfa23b84aae0fafc.tar.gz
DO NOT MERGE Improve the loading speed of the call history list.
Test: build and load app. Bug: 77980012. Change-Id: I6e542beb12e7fe37967f41eead196a503f9f016f
-rw-r--r--Android.mk3
-rw-r--r--res/layout-h720dp/dialer_fragment.xml124
-rw-r--r--src/com/android/car/dialer/ContactEntry.java191
-rw-r--r--src/com/android/car/dialer/StrequentsAdapter.java9
-rw-r--r--src/com/android/car/dialer/TelecomActivity.java10
-rw-r--r--src/com/android/car/dialer/telecom/InMemoryPhoneBook.java121
-rw-r--r--src/com/android/car/dialer/telecom/TelecomUtils.java47
-rw-r--r--src/com/android/car/dialer/ui/CallHistoryListItemProvider.java3
-rw-r--r--src/com/android/car/dialer/ui/CallLogListingTask.java162
-rw-r--r--src/com/android/car/dialer/ui/DialerInfoFragment.java168
-rw-r--r--src/com/android/car/dialer/ui/listitem/CallLogListItem.java29
-rw-r--r--src/com/android/car/dialer/ui/listitem/ContactListItem.java23
12 files changed, 692 insertions, 198 deletions
diff --git a/Android.mk b/Android.mk
index c656d927..c4084f7e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -40,7 +40,8 @@ LOCAL_STATIC_ANDROID_LIBRARIES += \
android-support-v7-cardview \
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-constraint-layout-solver
+ android-support-constraint-layout-solver \
+ guava \
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/res/layout-h720dp/dialer_fragment.xml b/res/layout-h720dp/dialer_fragment.xml
new file mode 100644
index 00000000..97099c55
--- /dev/null
+++ b/res/layout-h720dp/dialer_fragment.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+<!-- There seems to be a bug in layout inflation where it can't use a resource to inflate a view
+ group that sets layout_marginTop with a dimension. Work around by putting in a shell layout.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:card_view="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <!-- This CardView is clickable so that clicks do not fall through to the fragment that
+ is underneath the dialer_fragment. -->
+ <android.support.v7.widget.CardView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="@dimen/car_app_bar_height"
+ android:clickable="true"
+ card_view:cardBackgroundColor="@color/car_card"
+ card_view:cardElevation="@dimen/dialer_card_elevation">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/number"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dialer_number_view_height"
+ android:paddingTop="@dimen/dialer_number_view_padding"
+ android:paddingBottom="@dimen/dialer_number_view_padding"
+ android:gravity="center"
+ android:focusable="true"
+ android:text="@string/dial_a_number"
+ android:layout_alignParentTop="true"
+ style="@style/TextAppearance.Car.Body1" />
+
+ <View
+ android:id="@+id/line_divider"
+ android:background="@color/car_list_divider"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/line_divider_height"
+ android:layout_marginLeft="@dimen/car_keyline_1"
+ android:layout_marginRight="@dimen/car_keyline_1"
+ android:layout_below="@id/number" />
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_marginTop="@dimen/dial_container_vertical_margin"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/line_divider" >
+
+ <FrameLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1" >
+
+ <ImageButton
+ android:id="@+id/call"
+ android:scaleType="center"
+ android:src="@drawable/ic_phone"
+ style="@style/DialpadPrimaryButton"
+ android:elevation="@dimen/call_fab_elevation"
+ android:layout_gravity="center" />
+ </FrameLayout>
+
+ <include
+ android:layout_gravity="center"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ layout="@layout/dialpad" />
+
+ <FrameLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1" >
+
+ <ImageButton
+ android:id="@+id/delete"
+ android:layout_width="@dimen/bksp_button_width"
+ android:layout_height="@dimen/bksp_button_width"
+ android:scaleType="centerInside"
+ android:src="@drawable/ic_backspace"
+ android:tint="@color/car_tint"
+ android:background="@drawable/dialpad_delete_button_background"
+ android:layout_gravity="center" />
+ </FrameLayout>
+ </LinearLayout>
+ </RelativeLayout>
+
+ <!-- This FrameLayout ensures that the back button is centered within the
+ exit_dialer_button despite the button's touch target
+ being smaller. -->
+ <FrameLayout
+ android:layout_gravity="start|top"
+ android:layout_width="@dimen/car_keyline_1"
+ android:layout_height="@dimen/car_keyline_1">
+
+ <ImageView
+ android:id="@+id/exit_dialer_button"
+ android:background="@drawable/dialpad_button_background"
+ android:layout_gravity="center"
+ android:layout_width="@dimen/car_touch_target_size"
+ android:layout_height="@dimen/car_touch_target_size"
+ android:scaleType="center"
+ android:tint="@color/car_tint"
+ android:src="@drawable/ic_down_outlined" />
+ </FrameLayout>
+ </android.support.v7.widget.CardView>
+</FrameLayout>
diff --git a/src/com/android/car/dialer/ContactEntry.java b/src/com/android/car/dialer/ContactEntry.java
index 34db70fb..2b21dffb 100644
--- a/src/com/android/car/dialer/ContactEntry.java
+++ b/src/com/android/car/dialer/ContactEntry.java
@@ -20,43 +20,109 @@ import android.database.Cursor;
import android.provider.ContactsContract;
import android.support.annotation.Nullable;
import android.text.TextUtils;
+import android.util.Log;
+
import com.android.car.dialer.telecom.PhoneLoader;
import com.android.car.dialer.telecom.TelecomUtils;
/**
* Encapsulates data about a phone Contact entry. Typically loaded from the local Contact store.
*/
+// TODO: Refactor to use Builder design pattern.
public class ContactEntry implements Comparable<ContactEntry> {
private final Context mContext;
+ /**
+ * An unique primary key for searching an entry.
+ */
+ private int mId;
+
+ /**
+ * Whether this contact entry is starred by user.
+ */
+ private boolean mIsStarred;
+
+ /**
+ * Contact-specific information about whether or not a contact has been pinned by the user at
+ * a particular position within the system contact application's user interface.
+ */
+ private int mPinnedPosition;
+
+ /**
+ * Phone number.
+ */
+ private String mNumber;
+
+ /**
+ * The display name.
+ */
@Nullable
- public String name;
- public String number;
- public boolean isStarred;
- public int pinnedPosition;
+ private String mDisplayName;
+
+ /**
+ * A URI that can be used to retrieve a thumbnail of the contact's photo.
+ */
+ private String mAvatarThumbnailUri;
+
+ /**
+ * A URI that can be used to retrieve the contact's full-size photo.
+ */
+ private String mAvatarUri;
+
+ /**
+ * An opaque value that contains hints on how to find the contact if its row id changed
+ * as a result of a sync or aggregation
+ */
+ private String mLookupKey;
+
+ /**
+ * The type of data, for example Home or Work.
+ */
+ private int mType;
+
+ /**
+ * The user defined label for the the contact method.
+ */
+ private String mLabel;
/**
* Parses a Contact entry for a Cursor loaded from the OS Strequents DB.
*/
public static ContactEntry fromCursor(Cursor cursor, Context context) {
- int nameColumn = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
+ int idColumnIndex = PhoneLoader.getIdColumnIndex(cursor);
int starredColumn = cursor.getColumnIndex(ContactsContract.Contacts.STARRED);
int pinnedColumn = cursor.getColumnIndex("pinned");
+ int displayNameColumnIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
+ int avatarUriColumnIndex = cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_URI);
+ int avatarThumbnailColumnIndex = cursor.getColumnIndex(
+ ContactsContract.Contacts.PHOTO_THUMBNAIL_URI);
+ int lookupKeyColumnIndex = cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
+ int typeColumnIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DATA2);
+ int labelColumnIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DATA3);
- String name = cursor.getString(nameColumn);
+ String name = cursor.getString(displayNameColumnIndex);
String number = PhoneLoader.getPhoneNumber(cursor, context.getContentResolver());
int starred = cursor.getInt(starredColumn);
int pinnedPosition = cursor.getInt(pinnedColumn);
- return new ContactEntry(context, name, number, starred > 0, pinnedPosition);
+ ContactEntry contactEntry = new ContactEntry(context, name, number, starred > 0,
+ pinnedPosition);
+ Log.i("test-jia", "phone book number is " + number);
+ contactEntry.setId(cursor.getInt(idColumnIndex));
+ contactEntry.setAvatarUri(cursor.getString(avatarUriColumnIndex));
+ contactEntry.setAvatarThumbnailUri(cursor.getString(avatarThumbnailColumnIndex));
+ contactEntry.setLookupKey(cursor.getString(lookupKeyColumnIndex));
+ contactEntry.setType(cursor.getInt(typeColumnIndex));
+ contactEntry.setLabel(cursor.getString(labelColumnIndex));
+ return contactEntry;
}
public ContactEntry(
Context context, String name, String number, boolean isStarred, int pinnedPosition) {
mContext = context;
- this.name = name;
- this.number = number;
- this.isStarred = isStarred;
- this.pinnedPosition = pinnedPosition;
+ this.mDisplayName = name;
+ this.mNumber = number;
+ this.mIsStarred = isStarred;
+ this.mPinnedPosition = pinnedPosition;
}
/**
@@ -64,13 +130,13 @@ public class ContactEntry implements Comparable<ContactEntry> {
* It takes into account the number associated with a name for fail cases.
*/
public String getDisplayName() {
- if (!TextUtils.isEmpty(name)) {
- return name;
+ if (!TextUtils.isEmpty(mDisplayName)) {
+ return mDisplayName;
}
if (isVoicemail()) {
return mContext.getResources().getString(R.string.voicemail);
} else {
- String displayName = TelecomUtils.getFormattedNumber(mContext, number);
+ String displayName = TelecomUtils.getFormattedNumber(mContext, mNumber);
if (TextUtils.isEmpty(displayName)) {
displayName = mContext.getString(R.string.unknown);
}
@@ -79,23 +145,23 @@ public class ContactEntry implements Comparable<ContactEntry> {
}
public boolean isVoicemail() {
- return number.equals(TelecomUtils.getVoicemailNumber(mContext));
+ return mNumber.equals(TelecomUtils.getVoicemailNumber(mContext));
}
@Override
public int compareTo(ContactEntry strequentContactEntry) {
- if (isStarred == strequentContactEntry.isStarred) {
- if (pinnedPosition == strequentContactEntry.pinnedPosition) {
- if (name == strequentContactEntry.name) {
- return compare(number, strequentContactEntry.number);
+ if (mIsStarred == strequentContactEntry.mIsStarred) {
+ if (mPinnedPosition == strequentContactEntry.mPinnedPosition) {
+ if (mDisplayName == strequentContactEntry.mDisplayName) {
+ return compare(mNumber, strequentContactEntry.mNumber);
}
- return compare(name, strequentContactEntry.name);
+ return compare(mDisplayName, strequentContactEntry.mDisplayName);
} else {
- if (pinnedPosition > 0 && strequentContactEntry.pinnedPosition > 0) {
- return pinnedPosition - strequentContactEntry.pinnedPosition;
+ if (mPinnedPosition > 0 && strequentContactEntry.mPinnedPosition > 0) {
+ return mPinnedPosition - strequentContactEntry.mPinnedPosition;
}
- if (pinnedPosition > 0) {
+ if (mPinnedPosition > 0) {
return -1;
}
@@ -103,7 +169,7 @@ public class ContactEntry implements Comparable<ContactEntry> {
}
}
- if (isStarred) {
+ if (mIsStarred) {
return -1;
}
@@ -114,10 +180,10 @@ public class ContactEntry implements Comparable<ContactEntry> {
public boolean equals(Object obj) {
if (obj instanceof ContactEntry) {
ContactEntry other = (ContactEntry) obj;
- if (compare(name, other.name) == 0
- && compare(number, other.number) == 0
- && isStarred == other.isStarred
- && pinnedPosition == other.pinnedPosition) {
+ if (compare(mDisplayName, other.mDisplayName) == 0
+ && compare(mNumber, other.mNumber) == 0
+ && mIsStarred == other.mIsStarred
+ && mPinnedPosition == other.mPinnedPosition) {
return true;
}
}
@@ -127,10 +193,10 @@ public class ContactEntry implements Comparable<ContactEntry> {
@Override
public int hashCode() {
int result = 17;
- result = 31 * result + (isStarred ? 1 : 0);
- result = 31 * result + pinnedPosition;
- result = 31 * result + (name == null ? 0 : name.hashCode());
- result = 31 * result + (number == null ? 0 : number.hashCode());
+ result = 31 * result + (mIsStarred ? 1 : 0);
+ result = 31 * result + mPinnedPosition;
+ result = 31 * result + (mDisplayName == null ? 0 : mDisplayName.hashCode());
+ result = 31 * result + (mNumber == null ? 0 : mNumber.hashCode());
return result;
}
@@ -145,4 +211,65 @@ public class ContactEntry implements Comparable<ContactEntry> {
return one.compareTo(two);
}
+
+ public int getId() {
+ return mId;
+ }
+
+ private void setId(int id) {
+ mId = id;
+ }
+
+ public String getAvatarUri() {
+ return mAvatarUri;
+ }
+
+ private void setAvatarUri(String avatarUri) {
+ mAvatarUri = avatarUri;
+ }
+
+ public String getLookupKey() {
+ return mLookupKey;
+ }
+
+ private void setLookupKey(String lookupKey) {
+ mLookupKey = lookupKey;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ private void setType(int type) {
+ mType = type;
+ }
+
+ public String getLabel() {
+ return mLabel;
+ }
+
+ private void setLabel(String label) {
+ mLabel = label;
+ }
+
+ public String getAvatarThumbnailUri() {
+ return mAvatarThumbnailUri;
+ }
+
+ private void setAvatarThumbnailUri(String avatarThumbnailUri) {
+ mAvatarThumbnailUri = avatarThumbnailUri;
+ }
+
+ public String getNumber() {
+ return mNumber;
+ }
+
+ public boolean isStarred() {
+ return mIsStarred;
+ }
+
+ public int getPinnedPosition() {
+ return mPinnedPosition;
+ }
+
}
diff --git a/src/com/android/car/dialer/StrequentsAdapter.java b/src/com/android/car/dialer/StrequentsAdapter.java
index b321e6ec..8d14019a 100644
--- a/src/com/android/car/dialer/StrequentsAdapter.java
+++ b/src/com/android/car/dialer/StrequentsAdapter.java
@@ -320,10 +320,9 @@ public class StrequentsAdapter extends RecyclerView.Adapter<CallLogViewHolder>
private void onBindView(final CallLogViewHolder viewHolder, final ContactEntry entry) {
viewHolder.itemView.setOnClickListener(v -> onViewClicked(viewHolder));
- final String number = entry.number;
- // TODO(mcrico): Why is being a voicemail related to not having a name?
- boolean isVoicemail = (entry.name == null)
- && (number.equals(TelecomUtils.getVoicemailNumber(mContext)));
+ final String number = entry.getNumber();
+ // TODO: Why is being a voicemail related to not having a name?
+ boolean isVoicemail = entry.isVoicemail();
String secondaryText = "";
if (!isVoicemail) {
secondaryText = String.valueOf(TelecomUtils.getTypeFromNumber(mContext, number));
@@ -338,7 +337,7 @@ public class StrequentsAdapter extends RecyclerView.Adapter<CallLogViewHolder>
TelecomUtils.setContactBitmapAsync(mContext, viewHolder.icon, displayName, number);
- if (entry.isStarred) {
+ if (entry.isStarred()) {
viewHolder.smallIcon.setVisibility(View.VISIBLE);
final int iconColor = mContext.getColor(android.R.color.white);
viewHolder.smallIcon.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN);
diff --git a/src/com/android/car/dialer/TelecomActivity.java b/src/com/android/car/dialer/TelecomActivity.java
index a4b8518c..0eeb7e8c 100644
--- a/src/com/android/car/dialer/TelecomActivity.java
+++ b/src/com/android/car/dialer/TelecomActivity.java
@@ -30,6 +30,7 @@ import androidx.car.drawer.CarDrawerActivity;
import androidx.car.drawer.CarDrawerAdapter;
import androidx.car.drawer.DrawerItemViewHolder;
+import com.android.car.dialer.telecom.InMemoryPhoneBook;
import com.android.car.dialer.telecom.PhoneLoader;
import com.android.car.dialer.telecom.UiCall;
import com.android.car.dialer.telecom.UiCallManager;
@@ -94,6 +95,8 @@ public class TelecomActivity extends CarDrawerActivity implements CallListener {
mUiCallManager = UiCallManager.init(getApplicationContext());
mUiBluetoothMonitor = new UiBluetoothMonitor(this);
+ InMemoryPhoneBook.init(getApplicationContext());
+
findViewById(R.id.search).setOnClickListener(
v -> startActivity(new Intent(this, ContactSearchActivity.class)));
}
@@ -105,6 +108,7 @@ public class TelecomActivity extends CarDrawerActivity implements CallListener {
Log.d(TAG, "onDestroy");
}
mUiBluetoothMonitor.tearDown();
+ InMemoryPhoneBook.tearDown();
mUiCallManager.tearDown();
mUiCallManager = null;
}
@@ -249,11 +253,7 @@ public class TelecomActivity extends CarDrawerActivity implements CallListener {
}
Fragment fragment = StrequentsFragment.newInstance(mUiCallManager);
- if (getCurrentFragment() instanceof DialerFragment) {
- setContentFragmentWithSlideAndDelayAnimation(fragment);
- } else {
- setContentFragmentWithFadeAnimation(fragment);
- }
+ setContentFragment(fragment);
}
private void showOngoingCallFragment() {
diff --git a/src/com/android/car/dialer/telecom/InMemoryPhoneBook.java b/src/com/android/car/dialer/telecom/InMemoryPhoneBook.java
new file mode 100644
index 00000000..3aaa21fc
--- /dev/null
+++ b/src/com/android/car/dialer/telecom/InMemoryPhoneBook.java
@@ -0,0 +1,121 @@
+package com.android.car.dialer.telecom;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.provider.ContactsContract;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.telephony.PhoneNumberUtils;
+
+import com.android.car.dialer.ContactEntry;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A singleton statically accessible helper class which pre-loads contacts list into memory so
+ * that they can be accessed more easily and quickly.
+ */
+public class InMemoryPhoneBook implements Loader.OnLoadCompleteListener<Cursor> {
+ private static InMemoryPhoneBook sInMemoryPhoneBook;
+
+ private final Context mContext;
+
+ private boolean mIsLoaded = false;
+ private List<ContactEntry> mContactEntries = new ArrayList<>();
+ Map<Integer, List<ContactEntry>> mIdToContactEntryMap;
+
+ private InMemoryPhoneBook(Context context) {
+ mContext = context;
+ }
+
+ public static InMemoryPhoneBook init(Context context) {
+ if (sInMemoryPhoneBook == null) {
+ sInMemoryPhoneBook = new InMemoryPhoneBook(context);
+ sInMemoryPhoneBook.onInit();
+ } else {
+ throw new IllegalStateException("Call teardown before reinitialized PhoneBook");
+ }
+ return get();
+ }
+
+ public static InMemoryPhoneBook get() {
+ if (sInMemoryPhoneBook != null) {
+ return sInMemoryPhoneBook;
+ } else {
+ throw new IllegalStateException("Call init before get InMemoryPhoneBook");
+ }
+ }
+
+ public static void tearDown() {
+ sInMemoryPhoneBook = null;
+ }
+
+ private void onInit() {
+ CursorLoader cursorLoader = createPhoneBookCursorLoader();
+ cursorLoader.registerListener(0, this);
+ cursorLoader.startLoading();
+ }
+
+ public boolean isLoaded() {
+ return mIsLoaded;
+ }
+
+ /**
+ * Returns a alphabetically sorted contact list.
+ */
+ public List<ContactEntry> getOrderedContactEntries() {
+ return mContactEntries;
+ }
+
+ @Nullable
+ public ContactEntry lookupContactEntry(String phoneNumber) {
+ for (ContactEntry contactEntry : mContactEntries) {
+ if (PhoneNumberUtils.compare(mContext, phoneNumber, contactEntry.getNumber())) {
+ return contactEntry;
+ }
+ }
+ return null;
+ }
+
+ public Map<Integer, List<ContactEntry>> getIdToContactEntryMap() {
+ if (mIdToContactEntryMap == null) {
+ mIdToContactEntryMap = new HashMap<>();
+ for (ContactEntry contactEntry : mContactEntries) {
+ List<ContactEntry> list;
+ if (mIdToContactEntryMap.containsKey(contactEntry.getId())) {
+ list = mIdToContactEntryMap.get(contactEntry.getId());
+ } else {
+ list = new ArrayList<>();
+ }
+ list.add(contactEntry);
+ }
+ }
+ return mIdToContactEntryMap;
+ }
+
+ @Override
+ public void onLoadComplete(@NonNull Loader<Cursor> loader, @Nullable Cursor cursor) {
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ mContactEntries.add(ContactEntry.fromCursor(cursor, mContext));
+ }
+ }
+ mIsLoaded = true;
+ }
+
+ private CursorLoader createPhoneBookCursorLoader() {
+ return new CursorLoader(mContext,
+ ContactsContract.Data.CONTENT_URI,
+ null,
+ ContactsContract.Data.MIMETYPE + " = '"
+ + ContactsContract.CommonDataKinds.Phone
+ .CONTENT_ITEM_TYPE + "'",
+ null,
+ ContactsContract.Contacts.DISPLAY_NAME + " ASC ");
+ }
+}
diff --git a/src/com/android/car/dialer/telecom/TelecomUtils.java b/src/com/android/car/dialer/telecom/TelecomUtils.java
index ffae7654..78335b70 100644
--- a/src/com/android/car/dialer/telecom/TelecomUtils.java
+++ b/src/com/android/car/dialer/telecom/TelecomUtils.java
@@ -38,6 +38,7 @@ import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.widget.ImageView;
+
import com.android.car.apps.common.CircleBitmapDrawable;
import com.android.car.apps.common.LetterTileDrawable;
import com.android.car.dialer.R;
@@ -48,7 +49,7 @@ import java.util.Locale;
public class TelecomUtils {
private final static String TAG = "Em.TelecomUtils";
- private static final String[] CONTACT_ID_PROJECTION = new String[] {
+ private static final String[] CONTACT_ID_PROJECTION = new String[]{
ContactsContract.PhoneLookup.DISPLAY_NAME,
ContactsContract.PhoneLookup.TYPE,
ContactsContract.PhoneLookup.LABEL,
@@ -73,6 +74,7 @@ public class TelecomUtils {
/**
* Return the contact id for the given contact id
+ *
* @param id the contact id to get the photo for
* @return the contact photo if it is found, null otherwise.
*/
@@ -97,6 +99,7 @@ public class TelecomUtils {
/**
* Return the contact id for the given phone number.
+ *
* @param number Caller phone number
* @return the contact id if it is found, 0 otherwise.
*/
@@ -115,8 +118,7 @@ public class TelecomUtils {
int id = cursor.getInt(cursor.getColumnIndex(ContactsContract.PhoneLookup._ID));
return id;
}
- }
- finally {
+ } finally {
if (cursor != null) {
cursor.close();
}
@@ -126,6 +128,7 @@ public class TelecomUtils {
/**
* Return the label for the given phone number.
+ *
* @param number Caller phone number
* @return the label if it is found, 0 otherwise.
*/
@@ -154,8 +157,7 @@ public class TelecomUtils {
Phone.getTypeLabel(res, type, label);
return typeLabel;
}
- }
- finally {
+ } finally {
if (cursor != null) {
cursor.close();
}
@@ -220,7 +222,8 @@ public class TelecomUtils {
return getDisplayName(context, number, null);
}
- private static String getDisplayName(Context context, String number, Uri gatewayOriginalAddress) {
+ private static String getDisplayName(Context context, String number,
+ Uri gatewayOriginalAddress) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "getDisplayName: " + number
+ ", gatewayOriginalAddress: " + gatewayOriginalAddress);
@@ -257,7 +260,7 @@ public class TelecomUtils {
String name = null;
try {
cursor = cr.query(uri,
- new String[] {ContactsContract.PhoneLookup.DISPLAY_NAME}, null, null, null);
+ new String[]{ContactsContract.PhoneLookup.DISPLAY_NAME}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
name = cursor.getString(0);
}
@@ -306,7 +309,7 @@ public class TelecomUtils {
*/
public static String callStateToUiString(Context context, int state) {
Resources res = context.getResources();
- switch(state) {
+ switch (state) {
case Call.STATE_ACTIVE:
return res.getString(R.string.call_state_call_active);
case Call.STATE_HOLDING:
@@ -348,23 +351,21 @@ public class TelecomUtils {
* @param number A key to have a consisten color per phone number.
* @return A worker task if a new one was needed to load the bitmap.
*/
- @Nullable public static ContactBitmapWorker setContactBitmapAsync(Context context,
+ @Nullable
+ public static ContactBitmapWorker setContactBitmapAsync(Context context,
final ImageView icon, final @Nullable String name, final String number) {
return ContactBitmapWorker.loadBitmap(context.getContentResolver(), icon, number,
- new ContactBitmapWorker.BitmapWorkerListener() {
- @Override
- public void onBitmapLoaded(@Nullable Bitmap bitmap) {
- Resources r = icon.getResources();
- if (bitmap != null) {
- icon.setScaleType(ImageView.ScaleType.CENTER_CROP);
- icon.setImageDrawable(new CircleBitmapDrawable(r, bitmap));
- } else {
- icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
- LetterTileDrawable letterTileDrawable = new LetterTileDrawable(r);
- letterTileDrawable.setContactDetails(name, number);
- letterTileDrawable.setIsCircular(true);
- icon.setImageDrawable(letterTileDrawable);
- }
+ bitmap -> {
+ Resources r = icon.getResources();
+ if (bitmap != null) {
+ icon.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ icon.setImageDrawable(new CircleBitmapDrawable(r, bitmap));
+ } else {
+ icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+ LetterTileDrawable letterTileDrawable = new LetterTileDrawable(r);
+ letterTileDrawable.setContactDetails(name, number);
+ letterTileDrawable.setIsCircular(true);
+ icon.setImageDrawable(letterTileDrawable);
}
});
}
diff --git a/src/com/android/car/dialer/ui/CallHistoryListItemProvider.java b/src/com/android/car/dialer/ui/CallHistoryListItemProvider.java
index 6dc0c3d4..2eaff9f9 100644
--- a/src/com/android/car/dialer/ui/CallHistoryListItemProvider.java
+++ b/src/com/android/car/dialer/ui/CallHistoryListItemProvider.java
@@ -23,6 +23,7 @@ import androidx.car.widget.ListItemProvider;
import androidx.car.widget.TextListItem;
import com.android.car.dialer.telecom.UiCallManager;
+import com.android.car.dialer.ui.listitem.CallLogListItem;
import java.util.ArrayList;
import java.util.List;
@@ -34,7 +35,7 @@ public class CallHistoryListItemProvider extends ListItemProvider {
public void setCallHistoryListItems(Context context,
List<CallLogListingTask.CallLogItem> items) {
for (CallLogListingTask.CallLogItem callLogItem : items) {
- TextListItem callLogListItem = new TextListItem(context);
+ TextListItem callLogListItem = new CallLogListItem(context, callLogItem);
callLogListItem.setPrimaryActionIcon(
new BitmapDrawable(context.getResources(), callLogItem.mIcon), true);
diff --git a/src/com/android/car/dialer/ui/CallLogListingTask.java b/src/com/android/car/dialer/ui/CallLogListingTask.java
index a4211d6b..5a73d1bb 100644
--- a/src/com/android/car/dialer/ui/CallLogListingTask.java
+++ b/src/com/android/car/dialer/ui/CallLogListingTask.java
@@ -15,9 +15,9 @@
*/
package com.android.car.dialer.ui;
-import android.content.ContentResolver;
+import static android.provider.ContactsContract.CommonDataKinds.Phone.getTypeLabel;
+
import android.content.Context;
-import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.os.AsyncTask;
@@ -27,15 +27,18 @@ import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.text.format.DateUtils;
-import com.android.car.apps.common.CircleBitmapDrawable;
-import com.android.car.apps.common.LetterTileDrawable;
+import com.android.car.dialer.ContactEntry;
import com.android.car.dialer.R;
+import com.android.car.dialer.telecom.InMemoryPhoneBook;
import com.android.car.dialer.telecom.PhoneLoader;
import com.android.car.dialer.telecom.TelecomUtils;
import java.util.ArrayList;
import java.util.List;
+/**
+ * Async task which loads call history.
+ */
public class CallLogListingTask extends AsyncTask<Void, Void, Void> {
public static class CallLogItem {
public final String mTitle;
@@ -55,10 +58,6 @@ public class CallLogListingTask extends AsyncTask<Void, Void, Void> {
void onLoadComplete(List<CallLogItem> items);
}
-
- // Like a constant but needs a context so not static.
- private final String VOICEMAIL_NUMBER;
-
private Context mContext;
private Cursor mCursor;
private List<CallLogItem> mItems;
@@ -70,7 +69,6 @@ public class CallLogListingTask extends AsyncTask<Void, Void, Void> {
mCursor = cursor;
mItems = new ArrayList<>(mCursor.getCount());
mListener = listener;
- VOICEMAIL_NUMBER = TelecomUtils.getVoicemailNumber(mContext);
}
private String maybeAppendCount(StringBuilder sb, int count) {
@@ -80,42 +78,21 @@ public class CallLogListingTask extends AsyncTask<Void, Void, Void> {
return sb.toString();
}
- private String getContactName(String cachedName, String number,
- int count, boolean isVoicemail) {
- if (cachedName != null) {
- return maybeAppendCount(new StringBuilder(cachedName), count);
- }
-
+ private String getContactName(String cachedName, String number, int count,
+ boolean isVoicemail) {
StringBuilder sb = new StringBuilder();
if (isVoicemail) {
sb.append(mContext.getString(R.string.voicemail));
+ } else if (cachedName != null) {
+ sb.append(cachedName);
+ } else if (!TextUtils.isEmpty(number)) {
+ sb.append(TelecomUtils.getFormattedNumber(mContext, number));
} else {
- String displayName = TelecomUtils.getDisplayName(mContext, number);
- if (TextUtils.isEmpty(displayName)) {
- displayName = mContext.getString(R.string.unknown);
- }
- sb.append(displayName);
+ sb.append(mContext.getString(R.string.unknown));
}
-
return maybeAppendCount(sb, count);
}
- private Bitmap getContactImage(Context context, ContentResolver contentResolver,
- String name, String number) {
- Resources r = context.getResources();
- int size = r.getDimensionPixelSize(R.dimen.dialer_menu_icon_container_width);
-
- Bitmap bitmap = TelecomUtils.getContactPhotoFromNumber(contentResolver, number);
- if (bitmap != null) {
- return new CircleBitmapDrawable(r, bitmap).toBitmap(size);
- }
-
- LetterTileDrawable letterTileDrawable = new LetterTileDrawable(r);
- letterTileDrawable.setContactDetails(name, number);
- letterTileDrawable.setIsCircular(true);
- return letterTileDrawable.toBitmap(size);
- }
-
private static CharSequence getRelativeTime(long millis) {
boolean validTimestamp = millis > 0;
@@ -126,71 +103,71 @@ public class CallLogListingTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
- ContentResolver resolver = mContext.getContentResolver();
-
- try {
- if (mCursor != null) {
- int cachedNameColumn = PhoneLoader.getNameColumnIndex(mCursor);
+ if (mCursor != null) {
+ try {
int numberColumn = PhoneLoader.getNumberColumnIndex(mCursor);
int dateColumn = mCursor.getColumnIndex(CallLog.Calls.DATE);
while (mCursor.moveToNext()) {
int count = 1;
String number = mCursor.getString(numberColumn);
-
- // We want to group calls to the same number into one so seek forward as many
+ // We want to group calls to the same number into one so seek
+ // forward as many
// entries as possible as long as the number is the same.
int position = mCursor.getPosition();
while (mCursor.moveToNext()) {
String nextNumber = mCursor.getString(numberColumn);
- if (equalNumbers(number, nextNumber)) {
+ if (PhoneNumberUtils.compare(mContext, number, nextNumber)) {
count++;
} else {
break;
}
}
+
mCursor.moveToPosition(position);
- boolean isVoicemail = number.equals(VOICEMAIL_NUMBER);
- String name = getContactName(mCursor.getString(cachedNameColumn),
- number, count, isVoicemail);
+ boolean isVoicemail = PhoneNumberUtils.isVoiceMailNumber(number);
+ ContactEntry contactEntry = InMemoryPhoneBook.get().lookupContactEntry(number);
+ String nameWithCount = getContactName(
+ contactEntry != null ? contactEntry.getDisplayName() : null,
+ number,
+ count,
+ isVoicemail);
- // Not sure why this is the only column checked here but I'm assuming this was
- // to work around some bug on some device.
+ // Not sure why this is the only column checked here but I'm
+ // assuming this was to work around some bug on some device.
long millis = dateColumn == -1 ? 0 : mCursor.getLong(dateColumn);
- StringBuffer secondaryText = new StringBuffer();
+ StringBuffer secondaryTextStringBuilder = new StringBuffer();
CharSequence relativeDate = getRelativeTime(millis);
- // Append the type (work, mobile etc.) if it isnt voicemail.
+ // Append the type (work, mobile etc.) if it isn't voicemail.
if (!isVoicemail) {
- CharSequence type = TelecomUtils.getTypeFromNumber(mContext, number);
- secondaryText.append(type);
+ CharSequence type = contactEntry != null
+ ? getTypeLabel(mContext.getResources(), contactEntry.getType(),
+ contactEntry.getLabel())
+ : "";
+ secondaryTextStringBuilder.append(type);
if (!TextUtils.isEmpty(type) && !TextUtils.isEmpty(relativeDate)) {
- secondaryText.append(", ");
+ secondaryTextStringBuilder.append(", ");
}
}
-
// Add in the timestamp.
if (relativeDate != null) {
- secondaryText.append(relativeDate);
+ secondaryTextStringBuilder.append(relativeDate);
}
- Bitmap contactImage = getContactImage(mContext, resolver, name, number);
-
- CallLogItem item =
- new CallLogItem(name, secondaryText.toString(), number, contactImage);
+ CallLogItem item = new CallLogItem(nameWithCount,
+ secondaryTextStringBuilder.toString(),
+ number, null);
mItems.add(item);
-
// Since we deduplicated count rows, we can move all the way to that row so the
// next iteration takes us to the row following the last duplicate row.
if (count > 1) {
mCursor.moveToPosition(position + count - 1);
}
}
- }
- } finally {
- if (mCursor != null) {
+ } finally {
mCursor.close();
}
}
@@ -201,59 +178,4 @@ public class CallLogListingTask extends AsyncTask<Void, Void, Void> {
protected void onPostExecute(Void aVoid) {
mListener.onLoadComplete(mItems);
}
-
- /**
- * Determines if the specified number is actually a URI
- * (i.e. a SIP address) rather than a regular PSTN phone number,
- * based on whether or not the number contains an "@" character.
- *
- * @return true if number contains @
- *
- * from android.telephony.PhoneNumberUtils
- */
- public static boolean isUriNumber(String number) {
- // Note we allow either "@" or "%40" to indicate a URI, in case
- // the passed-in string is URI-escaped. (Neither "@" nor "%40"
- // will ever be found in a legal PSTN number.)
- return number != null && (number.contains("@") || number.contains("%40"));
- }
-
- private static boolean equalNumbers(String number1, String number2) {
- if (isUriNumber(number1) || isUriNumber(number2)) {
- return compareSipAddresses(number1, number2);
- } else {
- return PhoneNumberUtils.compare(number1, number2);
- }
- }
-
- private static boolean compareSipAddresses(String number1, String number2) {
- if (number1 == null || number2 == null) {
- return number1 == null && number2 == null;
- }
-
- String[] address1 = splitSipAddress(number1);
- String[] address2 = splitSipAddress(number2);
-
- return address1[0].equals(address2[0]) && address1[1].equals(address2[1]);
- }
-
- /**
- * Splits a sip address on either side of the @ sign and returns both halves.
- * If there is no @ sign, user info will be number and rest will be empty string
- * @param number the sip number to split
- * @return a string array of size 2. Element 0 is the user info (left side of @ sign) and
- * element 1 is the rest (right side of @ sign).
- */
- private static String[] splitSipAddress(String number) {
- String[] values = new String[2];
- int index = number.indexOf('@');
- if (index == -1) {
- values[0] = number;
- values[1] = "";
- } else {
- values[0] = number.substring(0, index);
- values[1] = number.substring(index);
- }
- return values;
- }
}
diff --git a/src/com/android/car/dialer/ui/DialerInfoFragment.java b/src/com/android/car/dialer/ui/DialerInfoFragment.java
new file mode 100644
index 00000000..f9906563
--- /dev/null
+++ b/src/com/android/car/dialer/ui/DialerInfoFragment.java
@@ -0,0 +1,168 @@
+/*
+ * 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.car.dialer.ui;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.telecom.Call;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.TextView;
+
+import com.android.car.apps.common.FabDrawable;
+import com.android.car.dialer.R;
+import com.android.car.dialer.telecom.TelecomUtils;
+import com.android.car.dialer.telecom.UiCall;
+import com.android.car.dialer.telecom.UiCallManager;
+
+/**
+ * Holds dialer information such as dialed number and shows proper action based on current call
+ * state such as call/mute.
+ */
+public class DialerInfoFragment extends Fragment {
+ private static final String DIAL_NUMBER_KEY = "DIAL_NUMBER_KEY";
+ private static final int MAX_DIAL_NUMBER = 20;
+
+ private TextView mTitleView;
+ private TextView mBodyView;
+
+ private ImageButton mCallButton;
+ private ImageButton mDeleteButton;
+
+ private ImageButton mEndCallButton;
+ private ImageButton mMuteButton;
+
+ private final StringBuffer mNumber = new StringBuffer(MAX_DIAL_NUMBER);
+
+ public static DialerInfoFragment newInstance(@Nullable String dialNumber) {
+ DialerInfoFragment fragment = new DialerInfoFragment();
+
+ if (!TextUtils.isEmpty(dialNumber)) {
+ Bundle args = new Bundle();
+ args.putString(DIAL_NUMBER_KEY, dialNumber);
+ fragment.setArguments(args);
+ }
+
+ return fragment;
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View fragmentView = inflater.inflate(R.layout.dialer_info_fragment, container, false);
+ mTitleView = fragmentView.findViewById(R.id.title);
+ mBodyView = fragmentView.findViewById(R.id.body);
+ mCallButton = fragmentView.findViewById(R.id.call_button);
+ mDeleteButton = fragmentView.findViewById(R.id.delete_button);
+ mEndCallButton = fragmentView.findViewById(R.id.end_call_button);
+ mMuteButton = fragmentView.findViewById(R.id.mute_button);
+
+ FabDrawable answerCallDrawable = new FabDrawable(getContext());
+ answerCallDrawable.setFabAndStrokeColor(getContext().getColor(R.color.phone_call));
+ mCallButton.setBackground(answerCallDrawable);
+ mCallButton.setOnClickListener((unusedView) -> {
+ if (!TextUtils.isEmpty(mNumber.toString())) {
+ UiCallManager.get().safePlaceCall(mNumber.toString(), false);
+ }
+ });
+ mDeleteButton.setOnClickListener(v -> {
+ removeLastDigit();
+ });
+ mDeleteButton.setOnLongClickListener(v -> {
+ // Clear all on long-press
+ clearDialedNumber();
+ return true;
+ });
+
+ updateView();
+
+ Bundle args = getArguments();
+ if (args != null) {
+ clearDialedNumber();
+ appendDialedNumber(args.getString(DIAL_NUMBER_KEY));
+ }
+
+ return fragmentView;
+ }
+
+ /**
+ * Append more number to the end of dialed number.
+ */
+ public void appendDialedNumber(String number) {
+ mNumber.append(number);
+ mTitleView.setText(getFormattedNumber(mNumber.toString()));
+ }
+
+ /**
+ * Remove last digit of the dialed number. If there's no number left to delete, there's no
+ * operation to be take.
+ */
+ public void removeLastDigit() {
+ if (mNumber.length() != 0) {
+ mNumber.deleteCharAt(mNumber.length() - 1);
+ mTitleView.setText(getFormattedNumber(mNumber.toString()));
+ }
+ }
+
+ private void updateView() {
+ UiCall onGoingCall = UiCallManager.get().getPrimaryCall();
+ if (onGoingCall == null) {
+ showPreDialUi();
+ } else if (onGoingCall.getState() == Call.STATE_CONNECTING) {
+ showDialingUi(onGoingCall);
+ } else if (onGoingCall.getState() == Call.STATE_ACTIVE) {
+ showInCallUi();
+ }
+ }
+
+ private void showPreDialUi() {
+ mCallButton.setVisibility(View.VISIBLE);
+ mDeleteButton.setVisibility(View.VISIBLE);
+
+ mEndCallButton.setVisibility(View.GONE);
+ mMuteButton.setVisibility(View.GONE);
+ }
+
+ private void showDialingUi(UiCall uiCall) {
+ FabDrawable answerCallDrawable = new FabDrawable(getContext());
+ answerCallDrawable.setFabAndStrokeColor(getContext().getColor(R.color.phone_end_call));
+ mEndCallButton.setBackground(answerCallDrawable);
+ mEndCallButton.setVisibility(View.VISIBLE);
+ mMuteButton.setVisibility(View.VISIBLE);
+ mBodyView.setVisibility(View.VISIBLE);
+
+ mDeleteButton.setVisibility(View.GONE);
+ mCallButton.setVisibility(View.GONE);
+ }
+
+ private void showInCallUi() {
+ // TODO: Implement this function.
+ }
+
+ private String getFormattedNumber(String number) {
+ return TelecomUtils.getFormattedNumber(getContext(), number);
+ }
+
+ private void clearDialedNumber() {
+ mNumber.setLength(0);
+ mTitleView.setText(getFormattedNumber(mNumber.toString()));
+ }
+}
diff --git a/src/com/android/car/dialer/ui/listitem/CallLogListItem.java b/src/com/android/car/dialer/ui/listitem/CallLogListItem.java
index c0cb4143..72939c5a 100644
--- a/src/com/android/car/dialer/ui/listitem/CallLogListItem.java
+++ b/src/com/android/car/dialer/ui/listitem/CallLogListItem.java
@@ -16,10 +16,15 @@
package com.android.car.dialer.ui.listitem;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.drawable.BitmapDrawable;
import androidx.car.widget.TextListItem;
+import com.android.car.apps.common.CircleBitmapDrawable;
+import com.android.car.apps.common.LetterTileDrawable;
+import com.android.car.dialer.telecom.ContactBitmapWorker;
+import com.android.car.dialer.telecom.TelecomUtils;
import com.android.car.dialer.telecom.UiCallManager;
import com.android.car.dialer.ui.CallHistoryListItemProvider;
import com.android.car.dialer.ui.CallLogListingTask;
@@ -41,13 +46,21 @@ public class CallLogListItem extends TextListItem {
@Override
public void onBind(ViewHolder viewHolder) {
super.onBind(viewHolder);
- setPrimaryActionIcon(
- new BitmapDrawable(mContext.getResources(), mCallLogItem.mIcon), true);
- setTitle(mCallLogItem.mTitle);
- setBody(mCallLogItem.mText);
-
- viewHolder.itemView.setOnClickListener((v) -> {
- UiCallManager.get().safePlaceCall(mCallLogItem.mNumber, false);
- });
+ ContactBitmapWorker.loadBitmap(mContext.getContentResolver(), viewHolder.getPrimaryIcon(),
+ mCallLogItem.mNumber,
+ bitmap -> {
+ Resources r = mContext.getResources();
+ if (bitmap != null) {
+ setPrimaryActionIcon(new CircleBitmapDrawable(r, bitmap), true);
+ } else {
+ LetterTileDrawable letterTileDrawable = new LetterTileDrawable(r);
+ letterTileDrawable.setContactDetails(mCallLogItem.mTitle,
+ mCallLogItem.mNumber);
+ letterTileDrawable.setIsCircular(true);
+ setPrimaryActionIcon(letterTileDrawable, true);
+ }
+ // force rebind the view.
+ super.onBind(viewHolder);
+ });
}
}
diff --git a/src/com/android/car/dialer/ui/listitem/ContactListItem.java b/src/com/android/car/dialer/ui/listitem/ContactListItem.java
index 03c5575b..a076013b 100644
--- a/src/com/android/car/dialer/ui/listitem/ContactListItem.java
+++ b/src/com/android/car/dialer/ui/listitem/ContactListItem.java
@@ -16,10 +16,13 @@
package com.android.car.dialer.ui.listitem;
import android.content.Context;
+import android.content.res.Resources;
import androidx.car.widget.TextListItem;
-import com.android.car.dialer.telecom.TelecomUtils;
+import com.android.car.apps.common.CircleBitmapDrawable;
+import com.android.car.apps.common.LetterTileDrawable;
+import com.android.car.dialer.telecom.ContactBitmapWorker;
import com.android.car.dialer.ui.ContactListFragment;
/**
@@ -38,7 +41,21 @@ public class ContactListItem extends TextListItem {
@Override
public void onBind(ViewHolder viewHolder) {
super.onBind(viewHolder);
- TelecomUtils.setContactBitmapAsync(mContext, viewHolder.getPrimaryIcon(),
- mContactItem.mDisplayName, mContactItem.mNumber);
+ ContactBitmapWorker.loadBitmap(mContext.getContentResolver(), viewHolder.getPrimaryIcon(),
+ mContactItem.mNumber,
+ bitmap -> {
+ Resources r = mContext.getResources();
+ if (bitmap != null) {
+ setPrimaryActionIcon(new CircleBitmapDrawable(r, bitmap), true);
+ } else {
+ LetterTileDrawable letterTileDrawable = new LetterTileDrawable(r);
+ letterTileDrawable.setContactDetails(mContactItem.mDisplayName,
+ mContactItem.mNumber);
+ letterTileDrawable.setIsCircular(true);
+ setPrimaryActionIcon(letterTileDrawable, true);
+ }
+ // force rebind the view.
+ super.onBind(viewHolder);
+ });
}
}