summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/speeddial
diff options
context:
space:
mode:
authorcalderwoodra <calderwoodra@google.com>2018-04-10 14:45:16 -0700
committerCopybara-Service <copybara-piper@google.com>2018-04-10 15:42:13 -0700
commit2bee0528c1f42b698a606f24da4fa652ceb8d322 (patch)
tree7b31dcd02a359b27f32a7476c3ed94bd262f6d82 /java/com/android/dialer/speeddial
parent2bdb59f0392e54be3dc2c57c32ce126e1e4af7cf (diff)
Wire up SpeedDial fragment with SpeedDialUiItemLoader.
This change is mostly just a migration from a cursor loader and cursor to a listenable future and list of POJOs. Bug: 36841782 Test: tap PiperOrigin-RevId: 192349724 Change-Id: I37140dcc2e5e03bc5745573c0d777e18c4f1a880
Diffstat (limited to 'java/com/android/dialer/speeddial')
-rw-r--r--java/com/android/dialer/speeddial/FavoritesViewHolder.java82
-rw-r--r--java/com/android/dialer/speeddial/SpeedDialAdapter.java118
-rw-r--r--java/com/android/dialer/speeddial/SpeedDialCursor.java148
-rw-r--r--java/com/android/dialer/speeddial/SpeedDialFragment.java70
-rw-r--r--java/com/android/dialer/speeddial/StrequentContactsCursorLoader.java97
-rw-r--r--java/com/android/dialer/speeddial/SuggestionViewHolder.java61
-rw-r--r--java/com/android/dialer/speeddial/database/SpeedDialEntry.java1
-rw-r--r--java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java (renamed from java/com/android/dialer/speeddial/SpeedDialUiItem.java)2
-rw-r--r--java/com/android/dialer/speeddial/loader/SpeedDialUiItemLoader.java (renamed from java/com/android/dialer/speeddial/SpeedDialUiItemLoader.java)14
-rw-r--r--java/com/android/dialer/speeddial/loader/UiItemLoader.java32
-rw-r--r--java/com/android/dialer/speeddial/loader/UiItemLoaderComponent.java39
11 files changed, 261 insertions, 403 deletions
diff --git a/java/com/android/dialer/speeddial/FavoritesViewHolder.java b/java/com/android/dialer/speeddial/FavoritesViewHolder.java
index c25b05ead..92ffb0a46 100644
--- a/java/com/android/dialer/speeddial/FavoritesViewHolder.java
+++ b/java/com/android/dialer/speeddial/FavoritesViewHolder.java
@@ -17,13 +17,8 @@
package com.android.dialer.speeddial;
import android.content.Context;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
import android.support.v7.widget.RecyclerView;
-import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
@@ -31,8 +26,12 @@ import android.widget.FrameLayout;
import android.widget.QuickContactBadge;
import android.widget.TextView;
import com.android.dialer.common.Assert;
-import com.android.dialer.contactphoto.ContactPhotoManager;
-import com.android.dialer.lettertile.LetterTileDrawable;
+import com.android.dialer.glidephotomanager.GlidePhotoManagerComponent;
+import com.android.dialer.glidephotomanager.PhotoInfo;
+import com.android.dialer.speeddial.database.SpeedDialEntry.Channel;
+import com.android.dialer.speeddial.loader.SpeedDialUiItem;
+import java.util.ArrayList;
+import java.util.List;
/** ViewHolder for starred/favorite contacts in {@link SpeedDialFragment}. */
public class FavoritesViewHolder extends RecyclerView.ViewHolder
@@ -48,7 +47,7 @@ public class FavoritesViewHolder extends RecyclerView.ViewHolder
private boolean hasDefaultNumber;
private boolean isVideoCall;
private String number;
- private String lookupKey;
+ private List<Channel> channels;
public FavoritesViewHolder(View view, FavoriteContactsListener listener) {
super(view);
@@ -62,44 +61,37 @@ public class FavoritesViewHolder extends RecyclerView.ViewHolder
this.listener = listener;
}
- public void bind(Context context, Cursor cursor) {
- Assert.checkArgument(cursor.getInt(StrequentContactsCursorLoader.PHONE_STARRED) == 1);
- isVideoCall = false; // TODO(calderwoodra): get from disambig data
- number = cursor.getString(StrequentContactsCursorLoader.PHONE_NUMBER);
+ public void bind(Context context, SpeedDialUiItem speedDialUiItem) {
+ Assert.checkArgument(speedDialUiItem.isStarred());
- String name = cursor.getString(StrequentContactsCursorLoader.PHONE_DISPLAY_NAME);
- long contactId = cursor.getLong(StrequentContactsCursorLoader.PHONE_ID);
- lookupKey = cursor.getString(StrequentContactsCursorLoader.PHONE_LOOKUP_KEY);
- Uri contactUri = Contacts.getLookupUri(contactId, lookupKey);
+ nameView.setText(speedDialUiItem.name());
+ hasDefaultNumber = speedDialUiItem.defaultChannel() != null;
+ if (hasDefaultNumber) {
+ channels = new ArrayList<>();
+ isVideoCall = speedDialUiItem.defaultChannel().isVideoTechnology();
+ number = speedDialUiItem.defaultChannel().number();
+ phoneType.setText(speedDialUiItem.defaultChannel().label());
+ videoCallIcon.setVisibility(isVideoCall ? View.VISIBLE : View.GONE);
+ } else {
+ channels = speedDialUiItem.channels();
+ isVideoCall = false;
+ number = null;
+ phoneType.setText("");
+ videoCallIcon.setVisibility(View.GONE);
+ }
- String photoUri = cursor.getString(StrequentContactsCursorLoader.PHONE_PHOTO_URI);
- ContactPhotoManager.getInstance(context)
- .loadDialerThumbnailOrPhoto(
+ GlidePhotoManagerComponent.get(context)
+ .glidePhotoManager()
+ .loadQuickContactBadge(
photoView,
- contactUri,
- cursor.getLong(StrequentContactsCursorLoader.PHONE_PHOTO_ID),
- photoUri == null ? null : Uri.parse(photoUri),
- name,
- LetterTileDrawable.TYPE_DEFAULT);
- nameView.setText(name);
- phoneType.setText(getLabel(context.getResources(), cursor));
- videoCallIcon.setVisibility(isVideoCall ? View.VISIBLE : View.GONE);
-
- // TODO(calderwoodra): Update this to include communication avenues also
- hasDefaultNumber = cursor.getInt(StrequentContactsCursorLoader.PHONE_IS_SUPER_PRIMARY) != 0;
- }
-
- // TODO(calderwoodra): handle CNAP and cequint types.
- // TODO(calderwoodra): unify this into a utility method with CallLogAdapter#getNumberType
- private static String getLabel(Resources resources, Cursor cursor) {
- int numberType = cursor.getInt(StrequentContactsCursorLoader.PHONE_TYPE);
- String numberLabel = cursor.getString(StrequentContactsCursorLoader.PHONE_LABEL);
-
- // Returns empty label instead of "custom" if the custom label is empty.
- if (numberType == Phone.TYPE_CUSTOM && TextUtils.isEmpty(numberLabel)) {
- return "";
- }
- return (String) Phone.getTypeLabel(resources, numberType, numberLabel);
+ PhotoInfo.newBuilder()
+ .setPhotoId(speedDialUiItem.photoId())
+ .setPhotoUri(speedDialUiItem.photoUri())
+ .setName(speedDialUiItem.name())
+ .setLookupUri(
+ Contacts.getLookupUri(speedDialUiItem.contactId(), speedDialUiItem.lookupKey())
+ .toString())
+ .build());
}
@Override
@@ -107,7 +99,7 @@ public class FavoritesViewHolder extends RecyclerView.ViewHolder
if (hasDefaultNumber) {
listener.onClick(number, isVideoCall);
} else {
- listener.onAmbiguousContactClicked(lookupKey);
+ listener.onAmbiguousContactClicked(channels);
}
}
@@ -122,7 +114,7 @@ public class FavoritesViewHolder extends RecyclerView.ViewHolder
public interface FavoriteContactsListener {
/** Called when the user clicks on a favorite contact that doesn't have a default number. */
- void onAmbiguousContactClicked(String contactId);
+ void onAmbiguousContactClicked(List<Channel> channels);
/** Called when the user clicks on a favorite contact. */
void onClick(String number, boolean isVideoCall);
diff --git a/java/com/android/dialer/speeddial/SpeedDialAdapter.java b/java/com/android/dialer/speeddial/SpeedDialAdapter.java
index 5f7b68e5c..3312397c7 100644
--- a/java/com/android/dialer/speeddial/SpeedDialAdapter.java
+++ b/java/com/android/dialer/speeddial/SpeedDialAdapter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -16,19 +16,30 @@
package com.android.dialer.speeddial;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.GridLayoutManager.SpanSizeLookup;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.LayoutManager;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import com.android.dialer.common.Assert;
import com.android.dialer.speeddial.FavoritesViewHolder.FavoriteContactsListener;
import com.android.dialer.speeddial.HeaderViewHolder.SpeedDialHeaderListener;
-import com.android.dialer.speeddial.SpeedDialCursor.RowType;
import com.android.dialer.speeddial.SuggestionViewHolder.SuggestedContactsListener;
+import com.android.dialer.speeddial.loader.SpeedDialUiItem;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
/**
* RecyclerView adapter for {@link SpeedDialFragment}.
@@ -42,14 +53,26 @@ import com.android.dialer.speeddial.SuggestionViewHolder.SuggestedContactsListen
* <li>Suggested contacts
* </ol>
*/
-final class SpeedDialAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+@SuppressWarnings("AndroidApiChecker")
+@TargetApi(VERSION_CODES.N)
+public final class SpeedDialAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({RowType.STARRED_HEADER, RowType.SUGGESTION_HEADER, RowType.STARRED, RowType.SUGGESTION})
+ @interface RowType {
+ int STARRED_HEADER = 0;
+ int SUGGESTION_HEADER = 1;
+ int STARRED = 2;
+ int SUGGESTION = 3;
+ }
private final Context context;
private final FavoriteContactsListener favoritesListener;
private final SuggestedContactsListener suggestedListener;
private final SpeedDialHeaderListener headerListener;
- private SpeedDialCursor cursor;
+ private final Map<Integer, Integer> positionToRowTypeMap = new ArrayMap<>();
+ private List<SpeedDialUiItem> speedDialUiItems;
public SpeedDialAdapter(
Context context,
@@ -64,39 +87,45 @@ final class SpeedDialAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
@Override
public int getItemViewType(int position) {
- return cursor.getRowType(position);
+ return positionToRowTypeMap.get(position);
}
+ @NonNull
@Override
- public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
- if (viewType == RowType.STARRED) {
- return new FavoritesViewHolder(
- inflater.inflate(R.layout.favorite_item_layout, parent, false), favoritesListener);
- } else if (viewType == RowType.SUGGESTION) {
- return new SuggestionViewHolder(
- inflater.inflate(R.layout.suggestion_row_layout, parent, false), suggestedListener);
- } else if (viewType == RowType.HEADER) {
- return new HeaderViewHolder(
- inflater.inflate(R.layout.speed_dial_header_layout, parent, false), headerListener);
- } else {
- throw Assert.createIllegalStateFailException("Invalid viewType: " + viewType);
+ switch (viewType) {
+ case RowType.STARRED:
+ return new FavoritesViewHolder(
+ inflater.inflate(R.layout.favorite_item_layout, parent, false), favoritesListener);
+ case RowType.SUGGESTION:
+ return new SuggestionViewHolder(
+ inflater.inflate(R.layout.suggestion_row_layout, parent, false), suggestedListener);
+ case RowType.STARRED_HEADER:
+ case RowType.SUGGESTION_HEADER:
+ return new HeaderViewHolder(
+ inflater.inflate(R.layout.speed_dial_header_layout, parent, false), headerListener);
+ default:
+ throw Assert.createIllegalStateFailException("Invalid viewType: " + viewType);
}
}
@Override
- public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
- cursor.moveToPosition(position);
- switch (cursor.getRowType(position)) {
- case RowType.HEADER:
- ((HeaderViewHolder) holder).setHeaderText(cursor.getHeader());
- ((HeaderViewHolder) holder).showAddButton(cursor.hasFavorites() && position == 0);
- break;
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ switch (getItemViewType(position)) {
+ case RowType.STARRED_HEADER:
+ ((HeaderViewHolder) holder).setHeaderText(R.string.favorites_header);
+ ((HeaderViewHolder) holder).showAddButton(true);
+ return;
+ case RowType.SUGGESTION_HEADER:
+ ((HeaderViewHolder) holder).setHeaderText(R.string.suggestions_header);
+ ((HeaderViewHolder) holder).showAddButton(false);
+ return;
case RowType.STARRED:
- ((FavoritesViewHolder) holder).bind(context, cursor);
+ ((FavoritesViewHolder) holder).bind(context, speedDialUiItems.get(position - 1));
break;
case RowType.SUGGESTION:
- ((SuggestionViewHolder) holder).bind(context, cursor);
+ ((SuggestionViewHolder) holder).bind(context, speedDialUiItems.get(position - 2));
break;
default:
throw Assert.createIllegalStateFailException("Invalid view holder: " + holder);
@@ -105,15 +134,35 @@ final class SpeedDialAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
@Override
public int getItemCount() {
- return cursor == null || cursor.isClosed() ? 0 : cursor.getCount();
+ return positionToRowTypeMap.size();
}
- public void setCursor(SpeedDialCursor cursor) {
- this.cursor = cursor;
- notifyDataSetChanged();
+ public void setSpeedDialUiItems(List<SpeedDialUiItem> immutableSpeedDialUiItems) {
+ speedDialUiItems = new ArrayList<>();
+ speedDialUiItems.addAll(immutableSpeedDialUiItems);
+ speedDialUiItems.sort((o1, o2) -> Boolean.compare(o2.isStarred(), o1.isStarred()));
+ positionToRowTypeMap.clear();
+ if (speedDialUiItems.isEmpty()) {
+ return;
+ }
+
+ // Show the add favorites even if there are no favorite contacts
+ positionToRowTypeMap.put(0, RowType.STARRED_HEADER);
+ int positionOfSuggestionHeader = 1;
+ for (int i = 0; i < speedDialUiItems.size(); i++) {
+ if (speedDialUiItems.get(i).isStarred()) {
+ positionToRowTypeMap.put(i + 1, RowType.STARRED); // +1 for the header
+ positionOfSuggestionHeader++;
+ } else {
+ positionToRowTypeMap.put(i + 2, RowType.SUGGESTION); // +2 for both headers
+ }
+ }
+ if (!speedDialUiItems.get(speedDialUiItems.size() - 1).isStarred()) {
+ positionToRowTypeMap.put(positionOfSuggestionHeader, RowType.SUGGESTION_HEADER);
+ }
}
- LayoutManager getLayoutManager(Context context) {
+ /* package-private */ LayoutManager getLayoutManager(Context context) {
GridLayoutManager layoutManager = new GridLayoutManager(context, 3 /* spanCount */);
layoutManager.setSpanSizeLookup(
new SpanSizeLookup() {
@@ -127,15 +176,16 @@ final class SpeedDialAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
@VisibleForTesting
int getSpanSize(int position) {
- switch (cursor.getRowType(position)) {
+ switch (getItemViewType(position)) {
case RowType.SUGGESTION:
- case RowType.HEADER:
+ case RowType.STARRED_HEADER:
+ case RowType.SUGGESTION_HEADER:
return 3; // span the whole screen
case RowType.STARRED:
return 1; // span 1/3 of the screen
default:
throw Assert.createIllegalStateFailException(
- "Invalid row type: " + cursor.getRowType(position));
+ "Invalid row type: " + positionToRowTypeMap.get(position));
}
}
}
diff --git a/java/com/android/dialer/speeddial/SpeedDialCursor.java b/java/com/android/dialer/speeddial/SpeedDialCursor.java
deleted file mode 100644
index 1208daefd..000000000
--- a/java/com/android/dialer/speeddial/SpeedDialCursor.java
+++ /dev/null
@@ -1,148 +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.speeddial;
-
-import android.annotation.SuppressLint;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.database.MergeCursor;
-import android.support.annotation.IntDef;
-import android.support.annotation.StringRes;
-import com.android.dialer.common.Assert;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-
-/** Cursor for favorites contacts. */
-final class SpeedDialCursor extends MergeCursor {
-
- /**
- * Caps the speed dial list to contain at most 20 contacts, including favorites and suggestions.
- * It is only a soft limit though, for the case that there are more than 20 favorite contacts.
- */
- private static final int SPEED_DIAL_CONTACT_LIST_SOFT_LIMIT = 20;
-
- private static final String[] HEADER_CURSOR_PROJECTION = {"header"};
- private static final int HEADER_COLUMN_POSITION = 0;
- private boolean hasFavorites;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({RowType.HEADER, RowType.STARRED, RowType.SUGGESTION})
- @interface RowType {
- int HEADER = 0;
- int STARRED = 1;
- int SUGGESTION = 2;
- }
-
- public static SpeedDialCursor newInstance(Cursor strequentCursor) {
- if (strequentCursor == null || strequentCursor.getCount() == 0) {
- return null;
- }
- SpeedDialCursor cursor = new SpeedDialCursor(buildCursors(strequentCursor));
- strequentCursor.close();
- return cursor;
- }
-
- private static Cursor[] buildCursors(Cursor strequentCursor) {
- MatrixCursor starred = new MatrixCursor(StrequentContactsCursorLoader.PHONE_PROJECTION);
- MatrixCursor suggestions = new MatrixCursor(StrequentContactsCursorLoader.PHONE_PROJECTION);
-
- strequentCursor.moveToPosition(-1);
- while (strequentCursor.moveToNext()) {
- if (strequentCursor.getPosition() != 0) {
- long contactId = strequentCursor.getLong(StrequentContactsCursorLoader.PHONE_CONTACT_ID);
- int position = strequentCursor.getPosition();
- boolean duplicate = false;
- // Iterate backwards through the cursor to check that this isn't a duplicate contact
- // TODO(calderwoodra): improve this algorithm (currently O(n^2)).
- while (strequentCursor.moveToPrevious() && !duplicate) {
- duplicate |=
- strequentCursor.getLong(StrequentContactsCursorLoader.PHONE_CONTACT_ID) == contactId;
- }
- strequentCursor.moveToPosition(position);
- if (duplicate) {
- continue;
- }
- }
-
- if (strequentCursor.getInt(StrequentContactsCursorLoader.PHONE_STARRED) == 1) {
- StrequentContactsCursorLoader.addToCursor(starred, strequentCursor);
- } else if (starred.getCount() + suggestions.getCount() < SPEED_DIAL_CONTACT_LIST_SOFT_LIMIT) {
- // Since all starred contacts come before each non-starred contact, it's safe to assume that
- // this list will never exceed the soft limit unless there are more starred contacts than
- // the limit permits.
- StrequentContactsCursorLoader.addToCursor(suggestions, strequentCursor);
- }
- }
-
- List<Cursor> cursorList = new ArrayList<>();
- if (starred.getCount() > 0) {
- cursorList.add(createHeaderCursor(R.string.favorites_header));
- cursorList.add(starred);
- }
- if (suggestions.getCount() > 0) {
- cursorList.add(createHeaderCursor(R.string.suggestions_header));
- cursorList.add(suggestions);
- }
- return cursorList.toArray(new Cursor[cursorList.size()]);
- }
-
- private static Cursor createHeaderCursor(@StringRes int header) {
- MatrixCursor cursor = new MatrixCursor(HEADER_CURSOR_PROJECTION);
- cursor.newRow().add(HEADER_CURSOR_PROJECTION[HEADER_COLUMN_POSITION], header);
- return cursor;
- }
-
- @RowType
- int getRowType(int position) {
- moveToPosition(position);
- if (getColumnCount() == 1) {
- return RowType.HEADER;
- } else if (getInt(StrequentContactsCursorLoader.PHONE_STARRED) == 1) {
- return RowType.STARRED;
- } else {
- return RowType.SUGGESTION;
- }
- }
-
- @SuppressLint("DefaultLocale")
- @StringRes
- int getHeader() {
- if (getRowType(getPosition()) != RowType.HEADER) {
- throw Assert.createIllegalStateFailException(
- String.format("Current position (%d) is not a header.", getPosition()));
- }
- return getInt(HEADER_COLUMN_POSITION);
- }
-
- public boolean hasFavorites() {
- return hasFavorites;
- }
-
- private SpeedDialCursor(Cursor[] cursors) {
- super(cursors);
- for (Cursor cursor : cursors) {
- cursor.moveToFirst();
- if (cursor.getColumnCount() != 1
- && cursor.getInt(StrequentContactsCursorLoader.PHONE_STARRED) == 1) {
- hasFavorites = true;
- break;
- }
- }
- }
-}
diff --git a/java/com/android/dialer/speeddial/SpeedDialFragment.java b/java/com/android/dialer/speeddial/SpeedDialFragment.java
index 03a3c75bf..d1f195be1 100644
--- a/java/com/android/dialer/speeddial/SpeedDialFragment.java
+++ b/java/com/android/dialer/speeddial/SpeedDialFragment.java
@@ -17,23 +17,28 @@
package com.android.dialer.speeddial;
import android.content.Intent;
-import android.database.Cursor;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
-import android.support.v4.app.LoaderManager.LoaderCallbacks;
-import android.support.v4.content.Loader;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.dialer.callintent.CallInitiationType;
import com.android.dialer.callintent.CallIntentBuilder;
-import com.android.dialer.common.Assert;
import com.android.dialer.precall.PreCall;
import com.android.dialer.speeddial.FavoritesViewHolder.FavoriteContactsListener;
import com.android.dialer.speeddial.HeaderViewHolder.SpeedDialHeaderListener;
import com.android.dialer.speeddial.SuggestionViewHolder.SuggestedContactsListener;
+import com.android.dialer.speeddial.database.SpeedDialEntry.Channel;
+import com.android.dialer.speeddial.loader.SpeedDialUiItem;
+import com.android.dialer.speeddial.loader.UiItemLoaderComponent;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.List;
/**
* Fragment for displaying:
@@ -47,13 +52,9 @@ import com.android.dialer.speeddial.SuggestionViewHolder.SuggestedContactsListen
*/
public class SpeedDialFragment extends Fragment {
- private static final int STREQUENT_CONTACTS_LOADER_ID = 1;
-
private final SpeedDialHeaderListener headerListener = new SpeedDialFragmentHeaderListener();
private final FavoriteContactsListener favoritesListener = new SpeedDialFavoritesListener();
private final SuggestedContactsListener suggestedListener = new SpeedDialSuggestedListener();
- private final SpeedDialFragmentLoaderCallback loaderCallback =
- new SpeedDialFragmentLoaderCallback();
private SpeedDialAdapter adapter;
@@ -72,7 +73,6 @@ public class SpeedDialFragment extends Fragment {
new SpeedDialAdapter(getContext(), favoritesListener, suggestedListener, headerListener);
recyclerView.setLayoutManager(adapter.getLayoutManager(getContext()));
recyclerView.setAdapter(adapter);
- getLoaderManager().initLoader(STREQUENT_CONTACTS_LOADER_ID, null /* args */, loaderCallback);
return view;
}
@@ -84,7 +84,28 @@ public class SpeedDialFragment extends Fragment {
@Override
public void onResume() {
super.onResume();
- getLoaderManager().restartLoader(STREQUENT_CONTACTS_LOADER_ID, null, loaderCallback);
+ Futures.addCallback(
+ UiItemLoaderComponent.get(getContext().getApplicationContext())
+ .speedDialUiItemLoader()
+ .loadSpeedDialUiItems(),
+ new FutureCallback<List<SpeedDialUiItem>>() {
+ @Override
+ public void onSuccess(List<SpeedDialUiItem> speedDialUiItems) {
+ // TODO(calderwoodra): this is bad
+ new Handler(Looper.getMainLooper())
+ .post(
+ () -> {
+ adapter.setSpeedDialUiItems(speedDialUiItems);
+ adapter.notifyDataSetChanged();
+ });
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ throw new RuntimeException(throwable);
+ }
+ },
+ MoreExecutors.directExecutor());
}
private class SpeedDialFragmentHeaderListener implements SpeedDialHeaderListener {
@@ -98,8 +119,8 @@ public class SpeedDialFragment extends Fragment {
private class SpeedDialFavoritesListener implements FavoriteContactsListener {
@Override
- public void onAmbiguousContactClicked(String lookupKey) {
- DisambigDialog.show(lookupKey, getFragmentManager());
+ public void onAmbiguousContactClicked(List<Channel> channels) {
+ // TODO(calderwoodra): implement the disambig dialog with channels
}
@Override
@@ -130,29 +151,4 @@ public class SpeedDialFragment extends Fragment {
getContext(), new CallIntentBuilder(number, CallInitiationType.Type.SPEED_DIAL));
}
}
-
- /**
- * Loader callback that registers a content observer. {@link #unregisterContentObserver()} needs
- * to be called during tear down of the fragment.
- */
- private class SpeedDialFragmentLoaderCallback implements LoaderCallbacks<Cursor> {
-
- @Override
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- if (id == STREQUENT_CONTACTS_LOADER_ID) {
- return new StrequentContactsCursorLoader(getContext());
- }
- throw Assert.createIllegalStateFailException("Invalid loader id: " + id);
- }
-
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- adapter.setCursor((SpeedDialCursor) data);
- }
-
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {
- adapter.setCursor(null);
- }
- }
}
diff --git a/java/com/android/dialer/speeddial/StrequentContactsCursorLoader.java b/java/com/android/dialer/speeddial/StrequentContactsCursorLoader.java
deleted file mode 100644
index a2dcfdc40..000000000
--- a/java/com/android/dialer/speeddial/StrequentContactsCursorLoader.java
+++ /dev/null
@@ -1,97 +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.speeddial;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.net.Uri;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts;
-import android.support.v4.content.CursorLoader;
-
-/** Cursor Loader for strequent contacts. */
-public final class StrequentContactsCursorLoader extends CursorLoader {
-
- static final int PHONE_ID = 0;
- static final int PHONE_DISPLAY_NAME = 1;
- static final int PHONE_STARRED = 2;
- static final int PHONE_PHOTO_URI = 3;
- static final int PHONE_LOOKUP_KEY = 4;
- static final int PHONE_PHOTO_ID = 5;
- static final int PHONE_NUMBER = 6;
- static final int PHONE_TYPE = 7;
- static final int PHONE_LABEL = 8;
- static final int PHONE_IS_SUPER_PRIMARY = 9;
- static final int PHONE_PINNED = 10;
- static final int PHONE_CONTACT_ID = 11;
-
- public static final String[] PHONE_PROJECTION =
- new String[] {
- Phone._ID, // 0
- Phone.DISPLAY_NAME, // 1
- Phone.STARRED, // 2
- Phone.PHOTO_URI, // 3
- Phone.LOOKUP_KEY, // 4
- Phone.PHOTO_ID, // 5
- Phone.NUMBER, // 6
- Phone.TYPE, // 7
- Phone.LABEL, // 8
- Phone.IS_SUPER_PRIMARY, // 9
- Phone.PINNED, // 10
- Phone.CONTACT_ID, // 11
- };
-
- StrequentContactsCursorLoader(Context context) {
- super(
- context,
- buildUri(),
- PHONE_PROJECTION,
- null /* selection */,
- null /* selectionArgs */,
- null /* sortOrder */);
- // TODO(calderwoodra): implement alternative display names
- }
-
- static void addToCursor(MatrixCursor dest, Cursor source) {
- dest.newRow()
- .add(PHONE_PROJECTION[PHONE_ID], source.getLong(PHONE_ID))
- .add(PHONE_PROJECTION[PHONE_DISPLAY_NAME], source.getString(PHONE_DISPLAY_NAME))
- .add(PHONE_PROJECTION[PHONE_STARRED], source.getInt(PHONE_STARRED))
- .add(PHONE_PROJECTION[PHONE_PHOTO_URI], source.getString(PHONE_PHOTO_URI))
- .add(PHONE_PROJECTION[PHONE_LOOKUP_KEY], source.getString(PHONE_LOOKUP_KEY))
- .add(PHONE_PROJECTION[PHONE_NUMBER], source.getString(PHONE_NUMBER))
- .add(PHONE_PROJECTION[PHONE_TYPE], source.getInt(PHONE_TYPE))
- .add(PHONE_PROJECTION[PHONE_LABEL], source.getString(PHONE_LABEL))
- .add(PHONE_PROJECTION[PHONE_IS_SUPER_PRIMARY], source.getInt(PHONE_IS_SUPER_PRIMARY))
- .add(PHONE_PROJECTION[PHONE_PINNED], source.getInt(PHONE_PINNED))
- .add(PHONE_PROJECTION[PHONE_CONTACT_ID], source.getLong(PHONE_CONTACT_ID));
- }
-
- private static Uri buildUri() {
- return Contacts.CONTENT_STREQUENT_URI
- .buildUpon()
- .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true")
- .build();
- }
-
- @Override
- public Cursor loadInBackground() {
- return SpeedDialCursor.newInstance(super.loadInBackground());
- }
-}
diff --git a/java/com/android/dialer/speeddial/SuggestionViewHolder.java b/java/com/android/dialer/speeddial/SuggestionViewHolder.java
index 213a54f55..9e4c81de8 100644
--- a/java/com/android/dialer/speeddial/SuggestionViewHolder.java
+++ b/java/com/android/dialer/speeddial/SuggestionViewHolder.java
@@ -17,10 +17,6 @@
package com.android.dialer.speeddial;
import android.content.Context;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
@@ -28,10 +24,12 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.widget.QuickContactBadge;
import android.widget.TextView;
-import com.android.dialer.contactphoto.ContactPhotoManager;
-import com.android.dialer.lettertile.LetterTileDrawable;
+import com.android.dialer.common.Assert;
+import com.android.dialer.glidephotomanager.GlidePhotoManagerComponent;
+import com.android.dialer.glidephotomanager.PhotoInfo;
import com.android.dialer.location.GeoUtil;
import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import com.android.dialer.speeddial.loader.SpeedDialUiItem;
/** ViewHolder for displaying suggested contacts in {@link SpeedDialFragment}. */
public class SuggestionViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
@@ -54,46 +52,35 @@ public class SuggestionViewHolder extends RecyclerView.ViewHolder implements OnC
this.listener = listener;
}
- public void bind(Context context, Cursor cursor) {
- number = cursor.getString(StrequentContactsCursorLoader.PHONE_NUMBER);
- number = PhoneNumberHelper.formatNumber(context, number, GeoUtil.getCurrentCountryIso(context));
+ public void bind(Context context, SpeedDialUiItem speedDialUiItem) {
+ Assert.isNotNull(speedDialUiItem.defaultChannel());
+ number =
+ PhoneNumberHelper.formatNumber(
+ context,
+ speedDialUiItem.defaultChannel().number(),
+ GeoUtil.getCurrentCountryIso(context));
- String name = cursor.getString(StrequentContactsCursorLoader.PHONE_DISPLAY_NAME);
- String label = getLabel(context.getResources(), cursor);
+ String label = speedDialUiItem.defaultChannel().label();
String secondaryInfo =
TextUtils.isEmpty(label)
? number
: context.getString(R.string.call_subject_type_and_number, label, number);
- nameOrNumberView.setText(name);
+ nameOrNumberView.setText(speedDialUiItem.name());
numberView.setText(secondaryInfo);
- long contactId = cursor.getLong(StrequentContactsCursorLoader.PHONE_ID);
- String lookupKey = cursor.getString(StrequentContactsCursorLoader.PHONE_LOOKUP_KEY);
- Uri contactUri = Contacts.getLookupUri(contactId, lookupKey);
-
- String photoUri = cursor.getString(StrequentContactsCursorLoader.PHONE_PHOTO_URI);
- ContactPhotoManager.getInstance(context)
- .loadDialerThumbnailOrPhoto(
+ GlidePhotoManagerComponent.get(context)
+ .glidePhotoManager()
+ .loadQuickContactBadge(
photoView,
- contactUri,
- cursor.getLong(StrequentContactsCursorLoader.PHONE_PHOTO_ID),
- photoUri == null ? null : Uri.parse(photoUri),
- name,
- LetterTileDrawable.TYPE_DEFAULT);
- }
-
- // TODO(calderwoodra): handle CNAP and cequint types.
- // TODO(calderwoodra): unify this into a utility method with CallLogAdapter#getNumberType
- private static String getLabel(Resources resources, Cursor cursor) {
- int numberType = cursor.getInt(StrequentContactsCursorLoader.PHONE_TYPE);
- String numberLabel = cursor.getString(StrequentContactsCursorLoader.PHONE_LABEL);
-
- // Returns empty label instead of "custom" if the custom label is empty.
- if (numberType == Phone.TYPE_CUSTOM && TextUtils.isEmpty(numberLabel)) {
- return "";
- }
- return (String) Phone.getTypeLabel(resources, numberType, numberLabel);
+ PhotoInfo.newBuilder()
+ .setPhotoId(speedDialUiItem.photoId())
+ .setPhotoUri(speedDialUiItem.photoUri())
+ .setName(speedDialUiItem.name())
+ .setLookupUri(
+ Contacts.getLookupUri(speedDialUiItem.contactId(), speedDialUiItem.lookupKey())
+ .toString())
+ .build());
}
@Override
diff --git a/java/com/android/dialer/speeddial/database/SpeedDialEntry.java b/java/com/android/dialer/speeddial/database/SpeedDialEntry.java
index 5b54b79c8..13ef4e306 100644
--- a/java/com/android/dialer/speeddial/database/SpeedDialEntry.java
+++ b/java/com/android/dialer/speeddial/database/SpeedDialEntry.java
@@ -88,6 +88,7 @@ public abstract class SpeedDialEntry {
public boolean isVideoTechnology() {
return technology() == IMS_VIDEO || technology() == DUO;
}
+
/**
* Raw phone number as the user entered it.
*
diff --git a/java/com/android/dialer/speeddial/SpeedDialUiItem.java b/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java
index 17552adf7..3381bf8c0 100644
--- a/java/com/android/dialer/speeddial/SpeedDialUiItem.java
+++ b/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.dialer.speeddial;
+package com.android.dialer.speeddial.loader;
import android.database.Cursor;
import android.provider.ContactsContract.CommonDataKinds.Phone;
diff --git a/java/com/android/dialer/speeddial/SpeedDialUiItemLoader.java b/java/com/android/dialer/speeddial/loader/SpeedDialUiItemLoader.java
index 13e5f8744..c23b67d45 100644
--- a/java/com/android/dialer/speeddial/SpeedDialUiItemLoader.java
+++ b/java/com/android/dialer/speeddial/loader/SpeedDialUiItemLoader.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.dialer.speeddial;
+package com.android.dialer.speeddial.loader;
import android.annotation.TargetApi;
import android.content.Context;
@@ -29,6 +29,7 @@ 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.common.concurrent.DialerFutureSerializer;
import com.android.dialer.inject.ApplicationContext;
import com.android.dialer.speeddial.database.SpeedDialEntry;
import com.android.dialer.speeddial.database.SpeedDialEntry.Channel;
@@ -40,6 +41,7 @@ import com.google.common.util.concurrent.ListeningExecutorService;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
+import javax.inject.Singleton;
/**
* Loads a list of {@link SpeedDialUiItem SpeedDialUiItems}.
@@ -62,10 +64,13 @@ import javax.inject.Inject;
*/
@SuppressWarnings("AndroidApiChecker")
@TargetApi(VERSION_CODES.N)
-public final class SpeedDialUiItemLoader {
+@Singleton
+public final class SpeedDialUiItemLoader implements UiItemLoader {
private final Context appContext;
private final ListeningExecutorService backgroundExecutor;
+ // Used to ensure that only one refresh flow runs at a time.
+ private final DialerFutureSerializer dialerFutureSerializer = new DialerFutureSerializer();
@Inject
public SpeedDialUiItemLoader(
@@ -80,8 +85,10 @@ public final class SpeedDialUiItemLoader {
* list is composed of starred contacts from {@link SpeedDialEntryDatabaseHelper} and suggestions
* from {@link Contacts#STREQUENT_PHONE_ONLY}.
*/
+ @Override
public ListenableFuture<ImmutableList<SpeedDialUiItem>> loadSpeedDialUiItems() {
- return backgroundExecutor.submit(this::doInBackground);
+ return dialerFutureSerializer.submitAsync(
+ () -> backgroundExecutor.submit(this::doInBackground), backgroundExecutor);
}
@WorkerThread
@@ -128,7 +135,6 @@ public final class SpeedDialUiItemLoader {
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())) {
diff --git a/java/com/android/dialer/speeddial/loader/UiItemLoader.java b/java/com/android/dialer/speeddial/loader/UiItemLoader.java
new file mode 100644
index 000000000..4b9a7319f
--- /dev/null
+++ b/java/com/android/dialer/speeddial/loader/UiItemLoader.java
@@ -0,0 +1,32 @@
+/*
+ * 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.speeddial.loader;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/** Provides operation for loading {@link SpeedDialUiItem SpeedDialUiItems} */
+public interface UiItemLoader {
+
+ /**
+ * Returns a {@link ListenableFuture} for a list of {@link SpeedDialUiItem SpeedDialUiItems}. This
+ * list is composed of starred contacts from {@link
+ * com.android.dialer.speeddial.database.SpeedDialEntryDatabaseHelper} and suggestions from {@link
+ * android.provider.ContactsContract.Contacts#STREQUENT_PHONE_ONLY}.
+ */
+ ListenableFuture<ImmutableList<SpeedDialUiItem>> loadSpeedDialUiItems();
+}
diff --git a/java/com/android/dialer/speeddial/loader/UiItemLoaderComponent.java b/java/com/android/dialer/speeddial/loader/UiItemLoaderComponent.java
new file mode 100644
index 000000000..7d01b4380
--- /dev/null
+++ b/java/com/android/dialer/speeddial/loader/UiItemLoaderComponent.java
@@ -0,0 +1,39 @@
+/*
+ * 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.speeddial.loader;
+
+import android.content.Context;
+import com.android.dialer.inject.HasRootComponent;
+import dagger.Subcomponent;
+
+/** Dagger component for the speeddial/loader package. */
+@Subcomponent
+public abstract class UiItemLoaderComponent {
+
+ public abstract SpeedDialUiItemLoader speedDialUiItemLoader();
+
+ public static UiItemLoaderComponent get(Context context) {
+ return ((UiItemLoaderComponent.HasComponent)
+ ((HasRootComponent) context.getApplicationContext()).component())
+ .uiItemLoaderComponent();
+ }
+
+ /** Used to refer to the root application component. */
+ public interface HasComponent {
+ UiItemLoaderComponent uiItemLoaderComponent();
+ }
+}