summaryrefslogtreecommitdiff
path: root/src/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/dialer/DialtactsActivity.java6
-rw-r--r--src/com/android/dialer/calllog/CallLogAdapter.java116
-rw-r--r--src/com/android/dialer/calllog/CallLogGroupBuilder.java136
-rw-r--r--src/com/android/dialer/calllog/CallLogListItemHelper.java3
-rw-r--r--src/com/android/dialer/calllog/CallLogListItemViewHolder.java118
-rw-r--r--src/com/android/dialer/calllog/GroupingListAdapter.java362
-rw-r--r--src/com/android/dialer/calllog/PromoCardViewHolder.java14
-rw-r--r--src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java95
-rw-r--r--src/com/android/dialer/dialpad/DialpadFragment.java5
-rw-r--r--src/com/android/dialer/filterednumber/FilterNumberDialogFragment.java154
10 files changed, 466 insertions, 543 deletions
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index f11f1b17a..4ade04af1 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -915,11 +915,11 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
return;
}
- final boolean phoneIsInUse = phoneIsInUse();
- if (phoneIsInUse || (intent.getData() != null && isDialIntent(intent))) {
+ final boolean showDialpadChooser = phoneIsInUse() && !DialpadFragment.isAddCallMode(intent);
+ if (showDialpadChooser || (intent.getData() != null && isDialIntent(intent))) {
showDialpadFragment(false);
mDialpadFragment.setStartedFromNewIntent(true);
- if (phoneIsInUse && !mDialpadFragment.isVisible()) {
+ if (showDialpadChooser && !mDialpadFragment.isVisible()) {
mInCallDialpadUp = true;
}
}
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index aa307b6c6..4d4d454d0 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -22,12 +22,10 @@ import android.content.SharedPreferences;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
-import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.support.v7.widget.RecyclerView;
import android.os.Bundle;
import android.os.Trace;
-import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.provider.CallLog;
import android.support.v7.widget.RecyclerView.ViewHolder;
@@ -35,28 +33,18 @@ import android.telecom.PhoneAccountHandle;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.util.Log;
-import android.view.ContextMenu;
import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.view.View.AccessibilityDelegate;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.ContextMenu.ContextMenuInfo;
import android.view.accessibility.AccessibilityEvent;
-import android.widget.TextView;
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.ClipboardUtils;
import com.android.contacts.common.util.PermissionsUtil;
-import com.android.dialer.DialtactsActivity;
import com.android.dialer.PhoneCallDetails;
import com.android.dialer.R;
import com.android.dialer.contactinfo.ContactInfoCache;
import com.android.dialer.contactinfo.ContactInfoCache.OnContactInfoChangedListener;
-import com.android.dialer.util.DialerUtils;
+import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
import com.android.dialer.util.PhoneNumberUtil;
import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
@@ -96,6 +84,7 @@ public class CallLogAdapter extends GroupingListAdapter
private final ContactInfoHelper mContactInfoHelper;
protected final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
private final CallFetcher mCallFetcher;
+ private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
protected ContactInfoCache mContactInfoCache;
@@ -131,7 +120,7 @@ public class CallLogAdapter extends GroupingListAdapter
private SharedPreferences mPrefs;
- private boolean mShowVoicemailPromoCard = false;
+ protected boolean mShowVoicemailPromoCard = false;
/** Instance of helper class for managing views. */
private final CallLogListItemHelper mCallLogListItemHelper;
@@ -195,89 +184,6 @@ public class CallLogAdapter extends GroupingListAdapter
}
};
- /**
- * Listener that is triggered to populate the context menu with actions to perform on the call's
- * number, when the call log entry is long pressed.
- */
- private final View.OnCreateContextMenuListener mOnCreateContextMenuListener =
- new View.OnCreateContextMenuListener() {
- @Override
- public void onCreateContextMenu(
- ContextMenu menu, View v, ContextMenuInfo menuInfo) {
- final CallLogListItemViewHolder vh = (CallLogListItemViewHolder) v.getTag();
- if (TextUtils.isEmpty(vh.number)) {
- return;
- }
-
- if (vh.callType == CallLog.Calls.VOICEMAIL_TYPE) {
- menu.setHeaderTitle(mContext.getResources().getText(R.string.voicemail));
- } else {
- menu.setHeaderTitle(vh.number);
- }
-
- final MenuItem copyItem = menu.add(
- ContextMenu.NONE,
- R.id.context_menu_copy_to_clipboard,
- ContextMenu.NONE,
- R.string.copy_number_text);
-
- copyItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- ClipboardUtils.copyText(mContext, null, vh.number, true);
- return true;
- }
- });
-
- // The edit number before call does not show up if any of the conditions apply:
- // 1) Number cannot be called
- // 2) Number is the voicemail number
- // 3) Number is a SIP address
-
- if (PhoneNumberUtil.canPlaceCallsTo(vh.number, vh.numberPresentation)
- && !mTelecomCallLogCache.isVoicemailNumber(vh.accountHandle, vh.number)
- && !PhoneNumberUtil.isSipNumber(vh.number)) {
- final MenuItem editItem = menu.add(
- ContextMenu.NONE,
- R.id.context_menu_edit_before_call,
- ContextMenu.NONE,
- R.string.call_log_edit_number_before_call);
-
- editItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- final Intent intent = new Intent(
- Intent.ACTION_DIAL, CallUtil.getCallUri(vh.number));
- intent.setClass(mContext, DialtactsActivity.class);
- DialerUtils.startActivityWithErrorToast(mContext, intent);
- return true;
- }
- });
- }
-
- final TextView transcriptView =
- vh.phoneCallDetailsViews.voicemailTranscriptionView;
- if (vh.callType == CallLog.Calls.VOICEMAIL_TYPE
- && transcriptView.length() > 0) {
- final MenuItem copyTranscriptItem = menu.add(
- ContextMenu.NONE,
- R.id.context_menu_copy_transcript_to_clipboard,
- ContextMenu.NONE,
- R.string.copy_transcript_text);
-
- copyTranscriptItem.setOnMenuItemClickListener(
- new OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- ClipboardUtils.copyText(
- mContext, null, transcriptView.getText(), true);
- return true;
- }
- });
- }
- }
- };
-
private void expandViewHolderActions(CallLogListItemViewHolder viewHolder) {
// If another item is expanded, notify it that it has changed. Its actions will be
// hidden when it is re-binded because we change mCurrentlyExpandedPosition below.
@@ -351,6 +257,8 @@ public class CallLogAdapter extends GroupingListAdapter
mCallLogGroupBuilder = new CallLogGroupBuilder(this);
mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
maybeShowVoicemailPromoCard();
+ mFilteredNumberAsyncQueryHandler =
+ new FilteredNumberAsyncQueryHandler(mContext.getContentResolver());
}
public void onSaveInstanceState(Bundle outState) {
@@ -439,12 +347,12 @@ public class CallLogAdapter extends GroupingListAdapter
mExpandCollapseListener,
mTelecomCallLogCache,
mCallLogListItemHelper,
- mVoicemailPlaybackPresenter);
+ mVoicemailPlaybackPresenter,
+ mFilteredNumberAsyncQueryHandler);
viewHolder.callLogEntryView.setTag(viewHolder);
viewHolder.callLogEntryView.setAccessibilityDelegate(mAccessibilityDelegate);
- viewHolder.primaryActionView.setOnCreateContextMenuListener(mOnCreateContextMenuListener);
viewHolder.primaryActionView.setTag(viewHolder);
return viewHolder;
@@ -628,6 +536,11 @@ public class CallLogAdapter extends GroupingListAdapter
? 1 : 0));
}
+ @Override
+ public int getGroupSize(int position) {
+ return super.getGroupSize(position - (mShowVoicemailPromoCard ? 1 : 0));
+ }
+
protected boolean isCallLogActivity() {
return mIsCallLogActivity;
}
@@ -794,11 +707,6 @@ public class CallLogAdapter extends GroupingListAdapter
mContactInfoCache.injectContactInfoForTest(number, countryIso, contactInfo);
}
- @Override
- public void addGroup(int cursorPosition, int size, boolean expanded) {
- super.addGroup(cursorPosition, size, expanded);
- }
-
/**
* Stores the day group associated with a call in the call log.
*
diff --git a/src/com/android/dialer/calllog/CallLogGroupBuilder.java b/src/com/android/dialer/calllog/CallLogGroupBuilder.java
index 0826aeb4a..4cf2d07cd 100644
--- a/src/com/android/dialer/calllog/CallLogGroupBuilder.java
+++ b/src/com/android/dialer/calllog/CallLogGroupBuilder.java
@@ -20,6 +20,7 @@ import android.database.Cursor;
import android.provider.CallLog.Calls;
import android.telephony.PhoneNumberUtils;
import android.text.format.Time;
+import android.text.TextUtils;
import com.android.contacts.common.util.DateUtils;
import com.android.contacts.common.util.PhoneNumberHelper;
@@ -46,9 +47,8 @@ public class CallLogGroupBuilder {
* dialed.
* @param cursorPosition The starting position of the group in the cursor.
* @param size The size of the group.
- * @param expanded Whether the group is expanded; always false for the call log.
*/
- public void addGroup(int cursorPosition, int size, boolean expanded);
+ public void addGroup(int cursorPosition, int size);
/**
* Defines the interface for tracking the day group each call belongs to. Calls in a call
@@ -94,7 +94,7 @@ public class CallLogGroupBuilder {
/**
* Finds all groups of adjacent entries in the call log which should be grouped together and
- * calls {@link GroupCreator#addGroup(int, int, boolean)} on {@link #mGroupCreator} for each of
+ * calls {@link GroupCreator#addGroup(int, int)} on {@link #mGroupCreator} for each of
* them.
* <p>
* For entries that are not grouped with others, we do not need to create a group of size one.
@@ -114,98 +114,70 @@ public class CallLogGroupBuilder {
// Get current system time, used for calculating which day group calls belong to.
long currentTime = System.currentTimeMillis();
-
- int currentGroupSize = 1;
cursor.moveToFirst();
- // The number of the first entry in the group.
- String firstNumber = cursor.getString(CallLogQuery.NUMBER);
- // This is the type of the first call in the group.
- int firstCallType = cursor.getInt(CallLogQuery.CALL_TYPE);
-
- // The account information of the first entry in the group.
- String firstAccountComponentName = cursor.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME);
- String firstAccountId = cursor.getString(CallLogQuery.ACCOUNT_ID);
// Determine the day group for the first call in the cursor.
final long firstDate = cursor.getLong(CallLogQuery.DATE);
final long firstRowId = cursor.getLong(CallLogQuery.ID);
- int currentGroupDayGroup = getDayGroup(firstDate, currentTime);
- mGroupCreator.setDayGroup(firstRowId, currentGroupDayGroup);
+ int groupDayGroup = getDayGroup(firstDate, currentTime);
+ mGroupCreator.setDayGroup(firstRowId, groupDayGroup);
- while (cursor.moveToNext()) {
- // The number of the current row in the cursor.
- final String currentNumber = cursor.getString(CallLogQuery.NUMBER);
- final int callType = cursor.getInt(CallLogQuery.CALL_TYPE);
- final String currentAccountComponentName = cursor.getString(
- CallLogQuery.ACCOUNT_COMPONENT_NAME);
- final String currentAccountId = cursor.getString(CallLogQuery.ACCOUNT_ID);
-
- final boolean sameNumber = equalNumbers(firstNumber, currentNumber);
- final boolean sameAccountComponentName = Objects.equals(
- firstAccountComponentName,
- currentAccountComponentName);
- final boolean sameAccountId = Objects.equals(
- firstAccountId,
- currentAccountId);
- final boolean sameAccount = sameAccountComponentName && sameAccountId;
-
- final boolean shouldGroup;
- final long currentCallId = cursor.getLong(CallLogQuery.ID);
- final long date = cursor.getLong(CallLogQuery.DATE);
-
- if (!sameNumber || !sameAccount) {
- // Should only group with calls from the same number.
- shouldGroup = false;
- } else if (firstCallType == Calls.VOICEMAIL_TYPE) {
- // never group voicemail.
- shouldGroup = false;
- } else {
- // Incoming, outgoing, and missed calls group together.
- shouldGroup = callType != Calls.VOICEMAIL_TYPE;
- }
+ // Instantiate the group values to those of the first call in the cursor.
+ String groupNumber = cursor.getString(CallLogQuery.NUMBER);
+ int groupCallType = cursor.getInt(CallLogQuery.CALL_TYPE);
+ String groupAccountComponentName = cursor.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME);
+ String groupAccountId = cursor.getString(CallLogQuery.ACCOUNT_ID);
+ int groupSize = 1;
+
+ String number;
+ int callType;
+ String accountComponentName;
+ String accountId;
- if (shouldGroup) {
+ while (cursor.moveToNext()) {
+ // Obtain the values for the current call to group.
+ number = cursor.getString(CallLogQuery.NUMBER);
+ callType = cursor.getInt(CallLogQuery.CALL_TYPE);
+ accountComponentName = cursor.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME);
+ accountId = cursor.getString(CallLogQuery.ACCOUNT_ID);
+
+ final boolean isSameNumber = equalNumbers(groupNumber, number);
+ final boolean isSameAccount = isSameAccount(
+ groupAccountComponentName, accountComponentName, groupAccountId, accountId);
+
+ // Group with the same number and account which are not voicemail.
+ if (isSameNumber && isSameAccount
+ && (callType != Calls.VOICEMAIL_TYPE)
+ && (groupCallType != Calls.VOICEMAIL_TYPE)) {
// Increment the size of the group to include the current call, but do not create
- // the group until we find a call that does not match.
- currentGroupSize++;
+ // the group until finding a call that does not match.
+ groupSize++;
} else {
- // The call group has changed, so determine the day group for the new call group.
- // This ensures all calls grouped together in the call log are assigned the same
- // day group.
- currentGroupDayGroup = getDayGroup(date, currentTime);
-
- // Create a group for the previous set of calls, excluding the current one, but do
- // not create a group for a single call.
- if (currentGroupSize > 1) {
- addGroup(cursor.getPosition() - currentGroupSize, currentGroupSize);
- }
+ // The call group has changed. Determine the day group for the new call group.
+ final long date = cursor.getLong(CallLogQuery.DATE);
+ groupDayGroup = getDayGroup(date, currentTime);
+
+ // Create a group for the previous group of calls, which does not include the
+ // current call.
+ mGroupCreator.addGroup(cursor.getPosition() - groupSize, groupSize);
+
// Start a new group; it will include at least the current call.
- currentGroupSize = 1;
- // The current entry is now the first in the group.
- firstNumber = currentNumber;
- firstCallType = callType;
- firstAccountComponentName = currentAccountComponentName;
- firstAccountId = currentAccountId;
+ groupSize = 1;
+
+ // Update the group values to those of the current call.
+ groupNumber = number;
+ groupCallType = callType;
+ groupAccountComponentName = accountComponentName;
+ groupAccountId = accountId;
}
// Save the day group associated with the current call.
- mGroupCreator.setDayGroup(currentCallId, currentGroupDayGroup);
- }
- // If the last set of calls at the end of the call log was itself a group, create it now.
- if (currentGroupSize > 1) {
- addGroup(count - currentGroupSize, currentGroupSize);
+ final long currentCallId = cursor.getLong(CallLogQuery.ID);
+ mGroupCreator.setDayGroup(currentCallId, groupDayGroup);
}
- }
- /**
- * Creates a group of items in the cursor.
- * <p>
- * The group is always unexpanded.
- *
- * @see CallLogAdapter#addGroup(int, int, boolean)
- */
- private void addGroup(int cursorPosition, int size) {
- mGroupCreator.addGroup(cursorPosition, size, false);
+ // Create a group for the last set of calls.
+ mGroupCreator.addGroup(count - groupSize, groupSize);
}
@VisibleForTesting
@@ -217,6 +189,10 @@ public class CallLogGroupBuilder {
}
}
+ private boolean isSameAccount(String name1, String name2, String id1, String id2) {
+ return TextUtils.equals(name1, name2) && TextUtils.equals(id1, id2);
+ }
+
@VisibleForTesting
boolean compareSipAddresses(String number1, String number2) {
if (number1 == null || number2 == null) return number1 == number2;
diff --git a/src/com/android/dialer/calllog/CallLogListItemHelper.java b/src/com/android/dialer/calllog/CallLogListItemHelper.java
index 1c8e397e4..d18e274ba 100644
--- a/src/com/android/dialer/calllog/CallLogListItemHelper.java
+++ b/src/com/android/dialer/calllog/CallLogListItemHelper.java
@@ -74,6 +74,9 @@ import com.android.dialer.R;
// Cache name or number of caller. Used when setting the content descriptions of buttons
// when the actions ViewStub is inflated.
views.nameOrNumber = getNameOrNumber(details);
+
+ // Cache country iso. Used for number filtering.
+ views.countryIso = details.countryIso;
}
/**
diff --git a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
index d85deb36f..521b2a429 100644
--- a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
+++ b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
@@ -21,12 +21,15 @@ import android.content.Context;
import android.content.res.Resources;
import android.content.Intent;
import android.net.Uri;
+import android.provider.CallLog;
import android.provider.CallLog.Calls;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.telecom.PhoneAccountHandle;
import android.text.TextUtils;
+import android.view.ContextMenu;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
@@ -34,12 +37,17 @@ import android.widget.QuickContactBadge;
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.contacts.common.CallUtil;
+import com.android.contacts.common.ClipboardUtils;
import com.android.contacts.common.ContactPhotoManager;
import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
import com.android.contacts.common.dialog.CallSubjectDialog;
import com.android.contacts.common.testing.NeededForTesting;
import com.android.contacts.common.util.UriUtils;
+import com.android.dialer.DialtactsActivity;
import com.android.dialer.R;
+import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.filterednumber.FilterNumberDialogFragment;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.util.PhoneNumberUtil;
import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
@@ -52,7 +60,8 @@ import com.android.dialer.voicemail.VoicemailPlaybackLayout;
* This object also contains UI logic pertaining to the view, to isolate it from the CallLogAdapter.
*/
public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
- implements View.OnClickListener {
+ implements View.OnClickListener, MenuItem.OnMenuItemClickListener,
+ View.OnCreateContextMenuListener {
/** The root view of the call log list item */
public final View rootView;
@@ -116,12 +125,24 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
public String numberType;
/**
+ * The country iso for the call. Cached here as the call back
+ * intent is set only when the actions ViewStub is inflated.
+ */
+ public String countryIso;
+
+ /**
* The type of call for the current call log entry. Cached here as the call back
* intent is set only when the actions ViewStub is inflated.
*/
public int callType;
/**
+ * ID for blocked numbers database.
+ * Set when context menu is created, if the number is blocked.
+ */
+ public Integer blockId;
+
+ /**
* The account for the current call log entry. Cached here as the call back
* intent is set only when the actions ViewStub is inflated.
*/
@@ -156,6 +177,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
private final TelecomCallLogCache mTelecomCallLogCache;
private final CallLogListItemHelper mCallLogListItemHelper;
private final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
+ private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
private final int mPhotoSize;
@@ -168,6 +190,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
TelecomCallLogCache telecomCallLogCache,
CallLogListItemHelper callLogListItemHelper,
VoicemailPlaybackPresenter voicemailPlaybackPresenter,
+ FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler,
View rootView,
QuickContactBadge quickContactView,
View primaryActionView,
@@ -182,6 +205,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
mTelecomCallLogCache = telecomCallLogCache;
mCallLogListItemHelper = callLogListItemHelper;
mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
+ mFilteredNumberAsyncQueryHandler = filteredNumberAsyncQueryHandler;
this.rootView = rootView;
this.quickContactView = quickContactView;
@@ -202,6 +226,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
primaryActionButtonView.setOnClickListener(this);
primaryActionView.setOnClickListener(mExpandCollapseListener);
+ primaryActionView.setOnCreateContextMenuListener(this);
}
public static CallLogListItemViewHolder create(
@@ -210,7 +235,8 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
View.OnClickListener expandCollapseListener,
TelecomCallLogCache telecomCallLogCache,
CallLogListItemHelper callLogListItemHelper,
- VoicemailPlaybackPresenter voicemailPlaybackPresenter) {
+ VoicemailPlaybackPresenter voicemailPlaybackPresenter,
+ FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler) {
return new CallLogListItemViewHolder(
context,
@@ -218,6 +244,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
telecomCallLogCache,
callLogListItemHelper,
voicemailPlaybackPresenter,
+ filteredNumberAsyncQueryHandler,
view,
(QuickContactBadge) view.findViewById(R.id.quick_contact_photo),
view.findViewById(R.id.primary_action_view),
@@ -227,6 +254,92 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
(ImageView) view.findViewById(R.id.primary_action_button));
}
+ @Override
+ public void onCreateContextMenu(
+ final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ if (TextUtils.isEmpty(number)) {
+ return;
+ }
+
+ if (callType == CallLog.Calls.VOICEMAIL_TYPE) {
+ menu.setHeaderTitle(mContext.getResources().getText(R.string.voicemail));
+ } else {
+ menu.setHeaderTitle(number);
+ }
+
+ menu.add(ContextMenu.NONE, R.id.context_menu_copy_to_clipboard, ContextMenu.NONE,
+ R.string.copy_number_text)
+ .setOnMenuItemClickListener(this);
+
+ // The edit number before call does not show up if any of the conditions apply:
+ // 1) Number cannot be called
+ // 2) Number is the voicemail number
+ // 3) Number is a SIP address
+
+ if (PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation)
+ && !mTelecomCallLogCache.isVoicemailNumber(accountHandle, number)
+ && !PhoneNumberUtil.isSipNumber(number)) {
+ menu.add(ContextMenu.NONE, R.id.context_menu_edit_before_call, ContextMenu.NONE,
+ R.string.call_log_edit_number_before_call)
+ .setOnMenuItemClickListener(this);
+ }
+
+ if (callType == CallLog.Calls.VOICEMAIL_TYPE
+ && phoneCallDetailsViews.voicemailTranscriptionView.length() > 0) {
+ menu.add(ContextMenu.NONE, R.id.context_menu_copy_transcript_to_clipboard,
+ ContextMenu.NONE, R.string.copy_transcript_text)
+ .setOnMenuItemClickListener(this);
+ }
+
+ try {
+ mFilteredNumberAsyncQueryHandler.isBlocked(
+ new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
+ @Override
+ public void onCheckComplete(Integer id) {
+ blockId = id;
+ int blockTitleId = blockId == null ? R.string.call_log_block_number
+ : R.string.call_log_unblock_number;
+ final MenuItem blockItem = menu.add(
+ ContextMenu.NONE,
+ R.id.context_menu_block_number,
+ ContextMenu.NONE,
+ blockTitleId);
+ blockItem.setOnMenuItemClickListener(
+ CallLogListItemViewHolder.this);
+ }
+ }, info.normalizedNumber, number, countryIso);
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.context_menu_block_number:
+ FilterNumberDialogFragment newFragment =
+ FilterNumberDialogFragment.newInstance(blockId, info.normalizedNumber,
+ number, countryIso, info.formattedNumber);
+ newFragment.setQueryHandler(mFilteredNumberAsyncQueryHandler);
+ newFragment.show(((Activity) mContext).getFragmentManager(),
+ FilterNumberDialogFragment.BLOCK_DIALOG_FRAGMENT);
+ return true;
+ case R.id.context_menu_copy_to_clipboard:
+ ClipboardUtils.copyText(mContext, null, number, true);
+ return true;
+ case R.id.context_menu_copy_transcript_to_clipboard:
+ ClipboardUtils.copyText(mContext, null,
+ phoneCallDetailsViews.voicemailTranscriptionView.getText(), true);
+ return true;
+ case R.id.context_menu_edit_before_call:
+ final Intent intent = new Intent(
+ Intent.ACTION_DIAL, CallUtil.getCallUri(number));
+ intent.setClass(mContext, DialtactsActivity.class);
+ DialerUtils.startActivityWithErrorToast(mContext, intent);
+ return true;
+ }
+ return false;
+ }
+
/**
* Configures the action buttons in the expandable actions ViewStub. The ViewStub is not
* inflated during initial binding, so click handlers, tags and accessibility text must be set
@@ -488,6 +601,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
telecomCallLogCache,
new CallLogListItemHelper(phoneCallDetailsHelper, resources, telecomCallLogCache),
null /* voicemailPlaybackPresenter */,
+ null /* filteredNumberAsyncQueryHandler */,
new View(context),
new QuickContactBadge(context),
new View(context),
diff --git a/src/com/android/dialer/calllog/GroupingListAdapter.java b/src/com/android/dialer/calllog/GroupingListAdapter.java
index 8d3ab4545..54dd5f679 100644
--- a/src/com/android/dialer/calllog/GroupingListAdapter.java
+++ b/src/com/android/dialer/calllog/GroupingListAdapter.java
@@ -22,78 +22,28 @@ import android.database.Cursor;
import android.database.DataSetObserver;
import android.os.Handler;
import android.support.v7.widget.RecyclerView;
-import android.util.Log;
import android.util.SparseIntArray;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-
-import com.android.contacts.common.testing.NeededForTesting;
/**
- * Maintains a list that groups adjacent items sharing the same value of a "group-by" field.
+ * Maintains a list that groups items into groups of consecutive elements which are disjoint,
+ * that is, an item can only belong to one group. This is leveraged for grouping calls in the
+ * call log received from or made to the same phone number.
*
- * The list has three types of elements: stand-alone, group header and group child. Groups are
- * collapsible and collapsed by default. This is used by the call log to group related entries.
+ * There are two integers stored as metadata for every list item in the adapter.
*/
abstract class GroupingListAdapter extends RecyclerView.Adapter {
- private static final int GROUP_METADATA_ARRAY_INITIAL_SIZE = 16;
- private static final int GROUP_METADATA_ARRAY_INCREMENT = 128;
- private static final long GROUP_OFFSET_MASK = 0x00000000FFFFFFFFL;
- private static final long GROUP_SIZE_MASK = 0x7FFFFFFF00000000L;
- private static final long EXPANDED_GROUP_MASK = 0x8000000000000000L;
-
- public static final int ITEM_TYPE_STANDALONE = 0;
- public static final int ITEM_TYPE_GROUP_HEADER = 1;
- public static final int ITEM_TYPE_IN_GROUP = 2;
-
- /**
- * Information about a specific list item: is it a group, if so is it expanded.
- * Otherwise, is it a stand-alone item or a group member.
- */
- protected static class PositionMetadata {
- int itemType;
- boolean isExpanded;
- int cursorPosition;
- int childCount;
- private int groupPosition;
- private int listPosition = -1;
- }
-
private Context mContext;
private Cursor mCursor;
/**
- * Count of list items.
- */
- private int mCount;
-
- private int mRowIdColumnIndex;
-
- /**
- * Count of groups in the list.
- */
- private int mGroupCount;
-
- /**
- * Information about where these groups are located in the list, how large they are
- * and whether they are expanded.
- */
- private long[] mGroupMetadata;
-
- private SparseIntArray mPositionCache = new SparseIntArray();
- private int mLastCachedListPosition;
- private int mLastCachedCursorPosition;
- private int mLastCachedGroup;
-
- /**
- * A reusable temporary instance of PositionMetadata
+ * SparseIntArray, which maps the cursor position of the first element of a group to the size
+ * of the group. The index of a key in this map corresponds to the list position of that group.
*/
- private PositionMetadata mPositionMetadata = new PositionMetadata();
+ private SparseIntArray mGroupMetadata;
+ private int mItemCount;
protected ContentObserver mChangeObserver = new ContentObserver(new Handler()) {
-
@Override
public boolean deliverSelfNotifications() {
return true;
@@ -106,7 +56,6 @@ abstract class GroupingListAdapter extends RecyclerView.Adapter {
};
protected DataSetObserver mDataSetObserver = new DataSetObserver() {
-
@Override
public void onChanged() {
notifyDataSetChanged();
@@ -115,7 +64,7 @@ abstract class GroupingListAdapter extends RecyclerView.Adapter {
public GroupingListAdapter(Context context) {
mContext = context;
- resetCache();
+ reset();
}
/**
@@ -126,18 +75,6 @@ abstract class GroupingListAdapter extends RecyclerView.Adapter {
protected abstract void onContentChanged();
- /**
- * Cache should be reset whenever the cursor changes or groups are expanded or collapsed.
- */
- private void resetCache() {
- mCount = -1;
- mLastCachedListPosition = -1;
- mLastCachedCursorPosition = -1;
- mLastCachedGroup = -1;
- mPositionMetadata.listPosition = -1;
- mPositionCache.clear();
- }
-
public void changeCursor(Cursor cursor) {
if (cursor == mCursor) {
return;
@@ -148,288 +85,73 @@ abstract class GroupingListAdapter extends RecyclerView.Adapter {
mCursor.unregisterDataSetObserver(mDataSetObserver);
mCursor.close();
}
+
+ // Reset whenever the cursor is changed.
+ reset();
mCursor = cursor;
- resetCache();
- findGroups();
if (cursor != null) {
+ addGroups(mCursor);
+
+ // Calculate the item count by subtracting group child counts from the cursor count.
+ mItemCount = mGroupMetadata.size();
+
cursor.registerContentObserver(mChangeObserver);
cursor.registerDataSetObserver(mDataSetObserver);
- mRowIdColumnIndex = cursor.getColumnIndexOrThrow("_id");
notifyDataSetChanged();
}
}
- @NeededForTesting
- public Cursor getCursor() {
- return mCursor;
- }
-
- /**
- * Scans over the entire cursor looking for duplicate phone numbers that need
- * to be collapsed.
- */
- private void findGroups() {
- mGroupCount = 0;
- mGroupMetadata = new long[GROUP_METADATA_ARRAY_INITIAL_SIZE];
-
- if (mCursor == null) {
- return;
- }
-
- addGroups(mCursor);
- }
-
/**
- * Records information about grouping in the list. Should be called by the overridden
- * {@link #addGroups} method.
+ * Records information about grouping in the list.
+ * Should be called by the overridden {@link #addGroups} method.
*/
- protected void addGroup(int cursorPosition, int size, boolean expanded) {
- if (mGroupCount >= mGroupMetadata.length) {
- int newSize = idealLongArraySize(
- mGroupMetadata.length + GROUP_METADATA_ARRAY_INCREMENT);
- long[] array = new long[newSize];
- System.arraycopy(mGroupMetadata, 0, array, 0, mGroupCount);
- mGroupMetadata = array;
- }
-
- long metadata = ((long)size << 32) | cursorPosition;
- if (expanded) {
- metadata |= EXPANDED_GROUP_MASK;
+ public void addGroup(int cursorPosition, int groupSize) {
+ int lastIndex = mGroupMetadata.size() - 1;
+ if (lastIndex < 0 || cursorPosition <= mGroupMetadata.keyAt(lastIndex)) {
+ mGroupMetadata.put(cursorPosition, groupSize);
+ } else {
+ // Optimization to avoid binary search if adding groups in ascending cursor position.
+ mGroupMetadata.append(cursorPosition, groupSize);
}
- mGroupMetadata[mGroupCount++] = metadata;
- }
-
- // Copy/paste from ArrayUtils
- private int idealLongArraySize(int need) {
- return idealByteArraySize(need * 8) / 8;
- }
-
- // Copy/paste from ArrayUtils
- private int idealByteArraySize(int need) {
- for (int i = 4; i < 32; i++)
- if (need <= (1 << i) - 12)
- return (1 << i) - 12;
-
- return need;
}
@Override
public int getItemCount() {
- if (mCursor == null) {
- return 0;
- }
-
- if (mCount != -1) {
- return mCount;
- }
-
- int cursorPosition = 0;
- int count = 0;
- for (int i = 0; i < mGroupCount; i++) {
- long metadata = mGroupMetadata[i];
- int offset = (int)(metadata & GROUP_OFFSET_MASK);
- boolean expanded = (metadata & EXPANDED_GROUP_MASK) != 0;
- int size = (int)((metadata & GROUP_SIZE_MASK) >> 32);
-
- count += (offset - cursorPosition);
-
- if (expanded) {
- count += size + 1;
- } else {
- count++;
- }
-
- cursorPosition = offset + size;
- }
-
- mCount = count + mCursor.getCount() - cursorPosition;
- return mCount;
+ return mItemCount;
}
/**
- * Figures out whether the item at the specified position represents a
- * stand-alone element, a group or a group child. Also computes the
- * corresponding cursor position.
+ * Given the position of a list item, returns the size of the group of items corresponding to
+ * that position.
*/
- public void obtainPositionMetadata(PositionMetadata metadata, int position) {
- // If the description object already contains requested information, just return
- if (metadata.listPosition == position) {
- return;
- }
-
- int listPosition = 0;
- int cursorPosition = 0;
- int firstGroupToCheck = 0;
-
- // Check cache for the supplied position. What we are looking for is
- // the group descriptor immediately preceding the supplied position.
- // Once we have that, we will be able to tell whether the position
- // is the header of the group, a member of the group or a standalone item.
- if (mLastCachedListPosition != -1) {
- if (position <= mLastCachedListPosition) {
-
- // Have SparceIntArray do a binary search for us.
- int index = mPositionCache.indexOfKey(position);
-
- // If we get back a positive number, the position corresponds to
- // a group header.
- if (index < 0) {
-
- // We had a cache miss, but we did obtain valuable information anyway.
- // The negative number will allow us to compute the location of
- // the group header immediately preceding the supplied position.
- index = ~index - 1;
-
- if (index >= mPositionCache.size()) {
- index--;
- }
- }
-
- // A non-negative index gives us the position of the group header
- // corresponding or preceding the position, so we can
- // search for the group information at the supplied position
- // starting with the cached group we just found
- if (index >= 0) {
- listPosition = mPositionCache.keyAt(index);
- firstGroupToCheck = mPositionCache.valueAt(index);
- long descriptor = mGroupMetadata[firstGroupToCheck];
- cursorPosition = (int)(descriptor & GROUP_OFFSET_MASK);
- }
- } else {
-
- // If we haven't examined groups beyond the supplied position,
- // we will start where we left off previously
- firstGroupToCheck = mLastCachedGroup;
- listPosition = mLastCachedListPosition;
- cursorPosition = mLastCachedCursorPosition;
- }
- }
-
- for (int i = firstGroupToCheck; i < mGroupCount; i++) {
- long group = mGroupMetadata[i];
- int offset = (int)(group & GROUP_OFFSET_MASK);
-
- // Move pointers to the beginning of the group
- listPosition += (offset - cursorPosition);
- cursorPosition = offset;
-
- if (i > mLastCachedGroup) {
- mPositionCache.append(listPosition, i);
- mLastCachedListPosition = listPosition;
- mLastCachedCursorPosition = cursorPosition;
- mLastCachedGroup = i;
- }
-
- // Now we have several possibilities:
- // A) The requested position precedes the group
- if (position < listPosition) {
- metadata.itemType = ITEM_TYPE_STANDALONE;
- metadata.cursorPosition = cursorPosition - (listPosition - position);
- metadata.childCount = 1;
- return;
- }
-
- boolean expanded = (group & EXPANDED_GROUP_MASK) != 0;
- int size = (int) ((group & GROUP_SIZE_MASK) >> 32);
-
- // B) The requested position is a group header
- if (position == listPosition) {
- metadata.itemType = ITEM_TYPE_GROUP_HEADER;
- metadata.groupPosition = i;
- metadata.isExpanded = expanded;
- metadata.childCount = size;
- metadata.cursorPosition = offset;
- return;
- }
-
- if (expanded) {
- // C) The requested position is an element in the expanded group
- if (position < listPosition + size + 1) {
- metadata.itemType = ITEM_TYPE_IN_GROUP;
- metadata.cursorPosition = cursorPosition + (position - listPosition) - 1;
- return;
- }
-
- // D) The element is past the expanded group
- listPosition += size + 1;
- } else {
-
- // E) The element is past the collapsed group
- listPosition++;
- }
-
- // Move cursor past the group
- cursorPosition += size;
+ public int getGroupSize(int listPosition) {
+ if (listPosition >= mGroupMetadata.size()) {
+ return 0;
}
- // The required item is past the last group
- metadata.itemType = ITEM_TYPE_STANDALONE;
- metadata.cursorPosition = cursorPosition + (position - listPosition);
- metadata.childCount = 1;
- }
-
- /**
- * Returns true if the specified position in the list corresponds to a
- * group header.
- */
- public boolean isGroupHeader(int position) {
- obtainPositionMetadata(mPositionMetadata, position);
- return mPositionMetadata.itemType == ITEM_TYPE_GROUP_HEADER;
+ return mGroupMetadata.valueAt(listPosition);
}
/**
- * Given a position of a groups header in the list, returns the size of
- * the corresponding group.
+ * Given the position of a list item, returns the the first item in the group of items
+ * corresponding to that position.
*/
- public int getGroupSize(int position) {
- obtainPositionMetadata(mPositionMetadata, position);
- return mPositionMetadata.childCount;
- }
-
- /**
- * Mark group as expanded if it is collapsed and vice versa.
- */
- @NeededForTesting
- public void toggleGroup(int position) {
- obtainPositionMetadata(mPositionMetadata, position);
- if (mPositionMetadata.itemType != ITEM_TYPE_GROUP_HEADER) {
- throw new IllegalArgumentException("Not a group at position " + position);
- }
-
- if (mPositionMetadata.isExpanded) {
- mGroupMetadata[mPositionMetadata.groupPosition] &= ~EXPANDED_GROUP_MASK;
- } else {
- mGroupMetadata[mPositionMetadata.groupPosition] |= EXPANDED_GROUP_MASK;
- }
- resetCache();
- notifyDataSetChanged();
- }
-
- public int getItemViewType(int position) {
- obtainPositionMetadata(mPositionMetadata, position);
- return mPositionMetadata.itemType;
- }
-
- public Object getItem(int position) {
- if (mCursor == null) {
+ public Object getItem(int listPosition) {
+ if (mCursor == null || listPosition >= mGroupMetadata.size()) {
return null;
}
- obtainPositionMetadata(mPositionMetadata, position);
- if (mCursor.moveToPosition(mPositionMetadata.cursorPosition)) {
+ int cursorPosition = mGroupMetadata.keyAt(listPosition);
+ if (mCursor.moveToPosition(cursorPosition)) {
return mCursor;
} else {
return null;
}
}
- public long getItemId(int position) {
- Object item = getItem(position);
- if (item != null) {
- return mCursor.getLong(mRowIdColumnIndex);
- } else {
- return -1;
- }
+ private void reset() {
+ mItemCount = 0;
+ mGroupMetadata = new SparseIntArray();
}
}
diff --git a/src/com/android/dialer/calllog/PromoCardViewHolder.java b/src/com/android/dialer/calllog/PromoCardViewHolder.java
index 4c9602759..656b66938 100644
--- a/src/com/android/dialer/calllog/PromoCardViewHolder.java
+++ b/src/com/android/dialer/calllog/PromoCardViewHolder.java
@@ -15,12 +15,14 @@
*/
package com.android.dialer.calllog;
-import com.android.dialer.R;
-
+import android.content.Context;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.View;
+import com.android.contacts.common.testing.NeededForTesting;
+import com.android.dialer.R;
+
/**
* View holder class for a promo card which will appear in the voicemail tab.
*/
@@ -68,4 +70,12 @@ public class PromoCardViewHolder extends RecyclerView.ViewHolder {
public View getOkTextView() {
return mOkTextView;
}
+
+ @NeededForTesting
+ public static PromoCardViewHolder createForTest(Context context) {
+ PromoCardViewHolder viewHolder = new PromoCardViewHolder(new View(context));
+ viewHolder.mSettingsTextView = new View(context);
+ viewHolder.mOkTextView = new View(context);
+ return viewHolder;
+ }
}
diff --git a/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java b/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
index 58a717b8b..2fdea0d13 100644
--- a/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
+++ b/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
@@ -21,6 +21,8 @@ import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDatabaseCorruptException;
import android.net.Uri;
import android.telephony.PhoneNumberUtils;
@@ -51,15 +53,28 @@ public class FilteredNumberAsyncQueryHandler extends AsyncQueryHandler {
}
public interface OnCheckBlockedListener {
- public void onQueryComplete(Integer id);
+ /**
+ * Invoked after querying if a number is blocked.
+ * @param id The ID of the row if blocked, null otherwise.
+ */
+ public void onCheckComplete(Integer id);
}
public interface OnBlockNumberListener {
- public void onInsertComplete(Uri uri);
+ /**
+ * Invoked after inserting a blocked number.
+ * @param uri The uri of the newly created row.
+ */
+ public void onBlockComplete(Uri uri);
}
public interface OnUnblockNumberListener {
- public void onDeleteComplete(int rows);
+ /**
+ * Invoked after removing a blocked number
+ * @param rows The number of rows affected (expected value 1).
+ * @param values The deleted data (used for restoration).
+ */
+ public void onUnblockComplete(int rows, ContentValues values);
}
@Override
@@ -93,7 +108,6 @@ public class FilteredNumberAsyncQueryHandler extends AsyncQueryHandler {
/**
* Check if the number + country iso given has been blocked.
* This method normalizes the number for the lookup if normalizedNumber is null.
- * Returns to the listener the the ID of the row if blocked, null otherwise.
*/
public final void isBlocked(final OnCheckBlockedListener listener,
String normalizedNumber, String number, String countryIso) {
@@ -108,25 +122,24 @@ public class FilteredNumberAsyncQueryHandler extends AsyncQueryHandler {
/**
* Check if the normalized number given has been blocked.
- * Returns to the listener the ID of the row if blocked, null otherwise.
*/
public final void isBlocked(final OnCheckBlockedListener listener,
- String normalizedNumber) {
+ String normalizedNumber) {
startQuery(NO_TOKEN,
new Listener() {
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
if (cursor.getCount() != 1) {
- listener.onQueryComplete(null);
+ listener.onCheckComplete(null);
return;
}
cursor.moveToFirst();
if (cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns.TYPE))
!= FilteredNumberTypes.BLOCKED_NUMBER) {
- listener.onQueryComplete(null);
+ listener.onCheckComplete(null);
return;
}
- listener.onQueryComplete(
+ listener.onCheckComplete(
cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns._ID)));
}
},
@@ -139,22 +152,11 @@ public class FilteredNumberAsyncQueryHandler extends AsyncQueryHandler {
/**
* Add a number manually blocked by the user.
- * Returns to the listener the URL of the newly created row.
*/
public final void blockNumber(final OnBlockNumberListener listener,
- String number, String countryIso) {
- blockNumber(listener,
- PhoneNumberUtils.formatNumberToE164(number, countryIso), number, countryIso);
- }
-
- /**
- * Add a number manually blocked by the user.
- * Returns to the listener the URL of the newly created row.
- */
- public final void blockNumber(final OnBlockNumberListener listener,
- String normalizedNumber, String number, String countryIso) {
+ String normalizedNumber, String number, String countryIso) {
if (normalizedNumber == null) {
- blockNumber(listener, number, countryIso);
+ normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
}
ContentValues v = new ContentValues();
v.put(FilteredNumberColumns.NORMALIZED_NUMBER, normalizedNumber);
@@ -162,30 +164,61 @@ public class FilteredNumberAsyncQueryHandler extends AsyncQueryHandler {
v.put(FilteredNumberColumns.COUNTRY_ISO, countryIso);
v.put(FilteredNumberColumns.TYPE, FilteredNumberTypes.BLOCKED_NUMBER);
v.put(FilteredNumberColumns.SOURCE, FilteredNumberSources.USER);
+ blockNumber(listener, v);
+ }
+
+ /**
+ * Block a number with specified ContentValues. Can be manually added or a restored row
+ * from performing the 'undo' action after unblocking.
+ */
+ public final void blockNumber(final OnBlockNumberListener listener, ContentValues values) {
startInsert(NO_TOKEN,
new Listener() {
@Override
public void onInsertComplete(int token, Object cookie, Uri uri) {
- listener.onInsertComplete(uri);
+ listener.onBlockComplete(uri);
}
- }, getContentUri(null), v);
+ }, getContentUri(null), values);
}
/**
* Removes row from database.
* Caller should call {@link FilteredNumberAsyncQueryHandler#isBlocked} first.
- * @param id the ID of the row to remove, from {@link FilteredNumberAsyncQueryHandler#isBlocked}.
- * Returns to the listener the number of rows affected. Expected value is 1.
+ * @param id The ID of row to remove, from {@link FilteredNumberAsyncQueryHandler#isBlocked}.
*/
public final void unblock(final OnUnblockNumberListener listener, Integer id) {
if (id == null) {
throw new IllegalArgumentException("Null id passed into unblock");
}
- startDelete(NO_TOKEN, new Listener() {
+ unblock(listener, getContentUri(id));
+ }
+
+ /**
+ * Removes row from database.
+ * @param uri The uri of row to remove, from
+ * {@link FilteredNumberAsyncQueryHandler#blockNumber}.
+ */
+ public final void unblock(final OnUnblockNumberListener listener, final Uri uri) {
+ startQuery(NO_TOKEN, new Listener() {
@Override
- public void onDeleteComplete(int token, Object cookie, int result) {
- listener.onDeleteComplete(result);
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ if (cursor.getCount() != 1) {
+ throw new SQLiteDatabaseCorruptException
+ ("Returned " + cursor.getCount() + " rows for uri "
+ + uri + "where 1 expected.");
+ }
+ cursor.moveToFirst();
+ final ContentValues values = new ContentValues();
+ DatabaseUtils.cursorRowToContentValues(cursor, values);
+ values.remove(FilteredNumberColumns._ID);
+
+ startDelete(NO_TOKEN, new Listener() {
+ @Override
+ public void onDeleteComplete(int token, Object cookie, int result) {
+ listener.onUnblockComplete(result, values);
+ }
+ }, uri, null, null);
}
- }, getContentUri(id), null, null);
+ }, uri, null, null, null, null);
}
-}
+} \ No newline at end of file
diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java
index 01dc892e5..0bbf802a4 100644
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ b/src/com/android/dialer/dialpad/DialpadFragment.java
@@ -481,7 +481,10 @@ public class DialpadFragment extends Fragment
* @param intent The intent.
* @return {@literal true} if add call operation was requested. {@literal false} otherwise.
*/
- private static boolean isAddCallMode(Intent intent) {
+ public static boolean isAddCallMode(Intent intent) {
+ if (intent == null) {
+ return false;
+ }
final String action = intent.getAction();
if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
// see if we are "adding a call" from the InCallScreen; false by default.
diff --git a/src/com/android/dialer/filterednumber/FilterNumberDialogFragment.java b/src/com/android/dialer/filterednumber/FilterNumberDialogFragment.java
new file mode 100644
index 000000000..f94d0f842
--- /dev/null
+++ b/src/com/android/dialer/filterednumber/FilterNumberDialogFragment.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2015 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.filterednumber;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.ContentValues;
+import android.content.DialogInterface;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.design.widget.Snackbar;
+import android.view.View;
+
+import com.android.dialer.R;
+import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
+
+public class FilterNumberDialogFragment extends DialogFragment {
+ public static final String BLOCK_DIALOG_FRAGMENT = "blockUnblockNumberDialog";
+
+ private static final String ARG_BLOCK_ID = "argBlockId";
+ private static final String ARG_NORMALIZED_NUMBER = "argNormalizedNumber";
+ private static final String ARG_NUMBER = "argNumber";
+ private static final String ARG_COUNTRY_ISO = "argCountryIso";
+ private static final String ARG_DISPLAY_NUMBER = "argDisplayNumber";
+
+ private FilteredNumberAsyncQueryHandler mHandler;
+
+ public void setQueryHandler (FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler) {
+ mHandler = filteredNumberAsyncQueryHandler;
+ }
+
+ public static FilterNumberDialogFragment newInstance(Integer blockId, String normalizedNumber,
+ String number, String countryIso, String displayNumber) {
+ final FilterNumberDialogFragment fragment = new FilterNumberDialogFragment();
+ final Bundle args = new Bundle();
+ if (blockId != null) {
+ args.putInt(ARG_BLOCK_ID, blockId.intValue());
+ }
+ args.putString(ARG_NORMALIZED_NUMBER, normalizedNumber);
+ args.putString(ARG_NUMBER, number);
+ args.putString(ARG_COUNTRY_ISO, countryIso);
+ args.putString(ARG_DISPLAY_NUMBER, displayNumber);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+ final boolean isBlocked = getArguments().containsKey(ARG_BLOCK_ID);
+ final String displayNumber = getArguments().getString(ARG_DISPLAY_NUMBER);
+
+ String message;
+ String okText;
+ if (isBlocked) {
+ message = getString(R.string.unblockNumberConfirmation, displayNumber);
+ okText = getString(R.string.unblockNumberOk);
+ } else {
+ message = getString(R.string.blockNumberConfirmation, displayNumber);
+ okText = getString(R.string.blockNumberOk);
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+ .setMessage(message)
+ .setPositiveButton(okText, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ if (isBlocked) {
+ unblockNumber();
+ } else {
+ blockNumber();
+ }
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null);
+ return builder.create();
+ }
+
+ public void blockNumber() {
+ final View view = getActivity().findViewById(R.id.floating_action_button_container);
+ final String displayNumber = getArguments().getString(ARG_DISPLAY_NUMBER);
+ final String message = getString(R.string.snackbar_number_blocked, displayNumber);
+ final String undoMessage = getString(R.string.snackbar_number_unblocked, displayNumber);
+ final FilteredNumberAsyncQueryHandler.OnUnblockNumberListener undoListener =
+ new FilteredNumberAsyncQueryHandler.OnUnblockNumberListener() {
+ @Override
+ public void onUnblockComplete(int rows, ContentValues values) {
+ Snackbar.make(view, undoMessage, Snackbar.LENGTH_LONG).show();
+ }
+ };
+
+ mHandler.blockNumber(
+ new FilteredNumberAsyncQueryHandler.OnBlockNumberListener() {
+ @Override
+ public void onBlockComplete(final Uri uri) {
+ Snackbar.make(view, message, Snackbar.LENGTH_LONG)
+ .setAction(R.string.block_number_undo,
+ // Delete the newly created row on 'undo'.
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mHandler.unblock(undoListener, uri);
+ }
+ })
+ .show();
+ }
+ }, getArguments().getString(ARG_NORMALIZED_NUMBER),
+ getArguments().getString(ARG_NUMBER), getArguments().getString(ARG_COUNTRY_ISO));
+ }
+
+ public void unblockNumber() {
+ final View view = getActivity().findViewById(R.id.floating_action_button_container);
+ final String displayNumber = getArguments().getString(ARG_DISPLAY_NUMBER);
+ final String message = getString(R.string.snackbar_number_unblocked, displayNumber);
+ final String undoMessage = getString(R.string.snackbar_number_blocked, displayNumber);
+ final FilteredNumberAsyncQueryHandler.OnBlockNumberListener undoListener =
+ new FilteredNumberAsyncQueryHandler.OnBlockNumberListener() {
+ @Override
+ public void onBlockComplete(final Uri uri) {
+ Snackbar.make(view, undoMessage, Snackbar.LENGTH_LONG).show();
+ }
+ };
+ mHandler.unblock(
+ new FilteredNumberAsyncQueryHandler.OnUnblockNumberListener() {
+ @Override
+ public void onUnblockComplete(int rows, final ContentValues values) {
+ Snackbar.make(view, message, Snackbar.LENGTH_LONG)
+ .setAction(R.string.block_number_undo,
+ new View.OnClickListener() {
+ // Re-insert the row on 'undo', with a new ID.
+ @Override
+ public void onClick(View view) {
+ mHandler.blockNumber(undoListener, values);
+ }
+ })
+ .show();
+ }
+ }, getArguments().getInt(ARG_BLOCK_ID));
+ }
+} \ No newline at end of file