summaryrefslogtreecommitdiff
path: root/src/com/android/dialer
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/dialer')
-rw-r--r--src/com/android/dialer/PhoneCallDetails.java3
-rw-r--r--src/com/android/dialer/calllog/BlockReportSpamListener.java124
-rw-r--r--src/com/android/dialer/calllog/CallLogAdapter.java141
-rw-r--r--src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java44
-rw-r--r--src/com/android/dialer/calllog/CallLogListItemHelper.java2
-rw-r--r--src/com/android/dialer/calllog/CallLogListItemViewHolder.java146
-rw-r--r--src/com/android/dialer/calllog/ContactInfoHelper.java24
-rw-r--r--src/com/android/dialer/calllog/MissedCallNotifier.java13
-rw-r--r--src/com/android/dialer/calllog/PhoneCallDetailsHelper.java6
-rw-r--r--src/com/android/dialer/compat/FilteredNumberCompat.java21
-rw-r--r--src/com/android/dialer/filterednumber/FilteredNumbersUtil.java3
-rw-r--r--src/com/android/dialer/service/ExtendedBlockingButtonRenderer.java86
-rw-r--r--src/com/android/dialer/service/ExtendedCallInfoService.java79
-rw-r--r--src/com/android/dialer/util/BlockReportSpamDialogs.java293
-rw-r--r--src/com/android/dialer/util/IntentUtil.java4
15 files changed, 791 insertions, 198 deletions
diff --git a/src/com/android/dialer/PhoneCallDetails.java b/src/com/android/dialer/PhoneCallDetails.java
index 17f1c2b64..8a2e52090 100644
--- a/src/com/android/dialer/PhoneCallDetails.java
+++ b/src/com/android/dialer/PhoneCallDetails.java
@@ -116,6 +116,9 @@ public class PhoneCallDetails {
*/
public boolean isRead = true;
+ // If this call is a spam number.
+ public boolean isSpam = false;
+
/**
* Constructor with required fields for the details of a call with a number associated with a
* contact.
diff --git a/src/com/android/dialer/calllog/BlockReportSpamListener.java b/src/com/android/dialer/calllog/BlockReportSpamListener.java
new file mode 100644
index 000000000..92cbc804b
--- /dev/null
+++ b/src/com/android/dialer/calllog/BlockReportSpamListener.java
@@ -0,0 +1,124 @@
+package com.android.dialer.calllog;
+
+import android.app.Activity;
+import android.app.FragmentManager;
+import android.content.ContentValues;
+import android.content.DialogInterface;
+import android.net.Uri;
+import android.support.v7.widget.RecyclerView;
+
+import com.android.dialer.util.BlockReportSpamDialogs;
+import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.service.ExtendedCallInfoService;
+
+/**
+ * Listener to show dialogs for block and report spam actions.
+ */
+public class BlockReportSpamListener implements CallLogListItemViewHolder.OnClickListener {
+
+ private final FragmentManager mFragmentManager;
+ private final RecyclerView.Adapter mAdapter;
+ private final ExtendedCallInfoService mExtendedCallInfoService;
+ private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
+
+ public BlockReportSpamListener(FragmentManager fragmentManager, RecyclerView.Adapter adapter,
+ ExtendedCallInfoService extendedCallInfoService,
+ FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler) {
+ mFragmentManager = fragmentManager;
+ mAdapter = adapter;
+ mExtendedCallInfoService = extendedCallInfoService;
+ mFilteredNumberAsyncQueryHandler = filteredNumberAsyncQueryHandler;
+ }
+
+ @Override
+ public void onBlockReportSpam(String displayNumber, final String number,
+ final String countryIso, final int callType) {
+ BlockReportSpamDialogs.BlockReportSpamDialogFragment.newInstance(
+ displayNumber,
+ false,
+ new BlockReportSpamDialogs.OnSpamDialogClickListener() {
+ @Override
+ public void onClick(boolean isSpamChecked) {
+ if (isSpamChecked) {
+ mExtendedCallInfoService.reportSpam(
+ number, countryIso, callType,
+ ExtendedCallInfoService.REPORTING_LOCATION_CALL_LOG_HISTORY);
+ }
+ mFilteredNumberAsyncQueryHandler.blockNumber(
+ new FilteredNumberAsyncQueryHandler.OnBlockNumberListener() {
+ @Override
+ public void onBlockComplete(Uri uri) {
+ mAdapter.notifyDataSetChanged();
+ }
+ },
+ number,
+ countryIso);
+ }
+ }, null)
+ .show(mFragmentManager, BlockReportSpamDialogs.BLOCK_REPORT_SPAM_DIALOG_TAG);
+ }
+
+ @Override
+ public void onBlock(String displayNumber, final String number, final String countryIso,
+ final int callType) {
+ BlockReportSpamDialogs.BlockDialogFragment.newInstance(displayNumber,
+ new BlockReportSpamDialogs.OnConfirmListener() {
+ @Override
+ public void onClick() {
+ mExtendedCallInfoService.reportSpam(number, countryIso, callType,
+ ExtendedCallInfoService.REPORTING_LOCATION_CALL_LOG_HISTORY);
+ mFilteredNumberAsyncQueryHandler.blockNumber(
+ new FilteredNumberAsyncQueryHandler.OnBlockNumberListener() {
+ @Override
+ public void onBlockComplete(Uri uri) {
+ mAdapter.notifyDataSetChanged();
+ }
+ },
+ number,
+ countryIso);
+ }
+ }, null)
+ .show(mFragmentManager, BlockReportSpamDialogs.BLOCK_DIALOG_TAG);
+ }
+
+ @Override
+ public void onUnblock(String displayNumber, final String number, final String countryIso,
+ final Integer blockId, final boolean isSpam, final int callType) {
+ BlockReportSpamDialogs.UnblockDialogFragment.newInstance(displayNumber, isSpam,
+ new BlockReportSpamDialogs.OnConfirmListener() {
+ @Override
+ public void onClick() {
+ if (isSpam) {
+ mExtendedCallInfoService.reportNotSpam(
+ number, countryIso, callType,
+ ExtendedCallInfoService.REPORTING_LOCATION_CALL_LOG_HISTORY);
+ }
+ mFilteredNumberAsyncQueryHandler.unblock(
+ new FilteredNumberAsyncQueryHandler.OnUnblockNumberListener() {
+ @Override
+ public void onUnblockComplete(int rows, ContentValues values) {
+ mAdapter.notifyDataSetChanged();
+ }
+ },
+ blockId);
+ }
+ }, null)
+ .show(mFragmentManager, BlockReportSpamDialogs.UNBLOCK_DIALOG_TAG);
+ }
+
+ @Override
+ public void onReportNotSpam(String displayNumber, final String number, final String countryIso,
+ final int callType) {
+ BlockReportSpamDialogs.ReportNotSpamDialogFragment.newInstance(displayNumber,
+ new BlockReportSpamDialogs.OnConfirmListener() {
+ @Override
+ public void onClick() {
+ mExtendedCallInfoService.reportNotSpam(
+ number, countryIso, callType,
+ ExtendedCallInfoService.REPORTING_LOCATION_CALL_LOG_HISTORY);
+ mAdapter.notifyDataSetChanged();
+ }
+ }, null)
+ .show(mFragmentManager, BlockReportSpamDialogs.NOT_SPAM_DIALOG_TAG);
+ }
+}
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index 3958611b9..9cde0b65d 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -16,8 +16,12 @@
package com.android.dialer.calllog;
+import com.android.dialer.filterednumber.BlockNumberDialogFragment;
+import com.android.dialer.service.ExtendedCallInfoService;
+import com.android.dialerbind.ObjectFactory;
import com.google.common.annotations.VisibleForTesting;
+import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -32,10 +36,8 @@ import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.telecom.PhoneAccountHandle;
-import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.AccessibilityDelegate;
@@ -55,23 +57,19 @@ import com.android.dialer.contactinfo.ContactInfoCache;
import com.android.dialer.contactinfo.ContactInfoCache.OnContactInfoChangedListener;
import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
import com.android.dialer.database.VoicemailArchiveContract;
-import com.android.dialer.filterednumber.BlockNumberDialogFragment.Callback;
import com.android.dialer.logging.InteractionEvent;
import com.android.dialer.logging.Logger;
-import com.android.dialer.service.ExtendedBlockingButtonRenderer;
import com.android.dialer.util.PhoneNumberUtil;
import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
import java.util.HashMap;
-import java.util.Map;
/**
* Adapter class to fill in data for the Call Log.
*/
public class CallLogAdapter extends GroupingListAdapter
implements CallLogGroupBuilder.GroupCreator,
- VoicemailPlaybackPresenter.OnVoicemailDeletedListener,
- ExtendedBlockingButtonRenderer.Listener {
+ VoicemailPlaybackPresenter.OnVoicemailDeletedListener {
// Types of activities the call log adapter is used for
public static final int ACTIVITY_TYPE_CALL_LOG = 1;
@@ -104,7 +102,6 @@ public class CallLogAdapter extends GroupingListAdapter
protected final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
private final CallFetcher mCallFetcher;
private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
- private final Map<String, Boolean> mBlockedNumberCache = new ArrayMap<>();
protected ContactInfoCache mContactInfoCache;
@@ -121,6 +118,27 @@ public class CallLogAdapter extends GroupingListAdapter
private int mHiddenPosition = RecyclerView.NO_POSITION;
private Uri mHiddenItemUri = null;
private boolean mPendingHide = false;
+ private BlockNumberDialogFragment.Callback mBlockedNumberDialogCallback =
+ new BlockNumberDialogFragment.Callback() {
+ @Override
+ public void onFilterNumberSuccess() {
+ Logger.logInteraction(
+ InteractionEvent.BLOCK_NUMBER_CALL_LOG);
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onUnfilterNumberSuccess() {
+ Logger.logInteraction(
+ InteractionEvent.UNBLOCK_NUMBER_CALL_LOG);
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onChangeFilteredNumberUndo() {
+ }
+ };
+ private CallLogListItemViewHolder.OnClickListener mBlockReportSpamListener;
/**
* Hashmap, keyed by call Id, used to track the day group for a call. As call log entries are
@@ -153,6 +171,8 @@ public class CallLogAdapter extends GroupingListAdapter
/** Helper to group call log entries. */
private final CallLogGroupBuilder mCallLogGroupBuilder;
+ private ExtendedCallInfoService mExtendedCallInfoService;
+
/**
* The OnClickListener used to expand or collapse the action buttons of a call log entry.
*/
@@ -296,6 +316,12 @@ public class CallLogAdapter extends GroupingListAdapter
mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
mContactsPreferences = new ContactsPreferences(mContext);
maybeShowVoicemailPromoCard();
+
+ mExtendedCallInfoService = ObjectFactory.newExtendedCallInfoService(context);
+ mBlockReportSpamListener = new BlockReportSpamListener(
+ ((Activity) mContext).getFragmentManager(), this,
+ mExtendedCallInfoService, mFilteredNumberAsyncQueryHandler);
+ setHasStableIds(true);
}
public void onSaveInstanceState(Bundle outState) {
@@ -312,24 +338,6 @@ public class CallLogAdapter extends GroupingListAdapter
}
}
- @Override
- public void onBlockedNumber(String number,String countryIso) {
- String cacheKey = PhoneNumberUtils.formatNumberToE164(number, countryIso);
- if (!TextUtils.isEmpty(cacheKey)) {
- mBlockedNumberCache.put(cacheKey, true);
- notifyDataSetChanged();
- }
- }
-
- @Override
- public void onUnblockedNumber( String number, String countryIso) {
- String cacheKey = PhoneNumberUtils.formatNumberToE164(number, countryIso);
- if (!TextUtils.isEmpty(cacheKey)) {
- mBlockedNumberCache.put(cacheKey, false);
- notifyDataSetChanged();
- }
- }
-
/**
* Requery on background thread when {@link Cursor} changes.
*/
@@ -406,28 +414,14 @@ public class CallLogAdapter extends GroupingListAdapter
CallLogListItemViewHolder viewHolder = CallLogListItemViewHolder.create(
view,
mContext,
- this,
+ mBlockReportSpamListener,
mExpandCollapseListener,
mCallLogCache,
mCallLogListItemHelper,
mVoicemailPlaybackPresenter,
mFilteredNumberAsyncQueryHandler,
- new Callback() {
- @Override
- public void onFilterNumberSuccess() {
- Logger.logInteraction(
- InteractionEvent.BLOCK_NUMBER_CALL_LOG);
- }
-
- @Override
- public void onUnfilterNumberSuccess() {
- Logger.logInteraction(
- InteractionEvent.UNBLOCK_NUMBER_CALL_LOG);
- }
-
- @Override
- public void onChangeFilteredNumberUndo() {}
- }, mActivityType == ACTIVITY_TYPE_ARCHIVE);
+ mBlockedNumberDialogCallback,
+ mActivityType == ACTIVITY_TYPE_ARCHIVE);
viewHolder.callLogEntryView.setTag(viewHolder);
viewHolder.callLogEntryView.setAccessibilityDelegate(mAccessibilityDelegate);
@@ -481,7 +475,42 @@ public class CallLogAdapter extends GroupingListAdapter
* @param position The position of the list item.
*/
- private void bindCallLogListViewHolder(ViewHolder viewHolder, int position) {
+ private void bindCallLogListViewHolder(final ViewHolder viewHolder, final int position) {
+ Cursor c = (Cursor) getItem(position);
+ if (c == null) {
+ return;
+ }
+
+ final String number = c.getString(CallLogQuery.NUMBER);
+ final String countryIso = c.getString(CallLogQuery.COUNTRY_ISO);
+ final CallLogListItemViewHolder views = (CallLogListItemViewHolder) viewHolder;
+ boolean success = mFilteredNumberAsyncQueryHandler.isBlockedNumber(
+ new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
+ @Override
+ public void onCheckComplete(Integer id) {
+ views.blockId = id;
+ if (mExtendedCallInfoService == null) {
+ loadDataAndRender(views);
+ } else {
+ views.isSpamFeatureEnabled = true;
+ mExtendedCallInfoService.getExtendedCallInfo(number, countryIso,
+ new ExtendedCallInfoService.Listener() {
+ @Override
+ public void onComplete(boolean isSpam) {
+ views.isSpam = isSpam;
+ loadDataAndRender(views);
+ }
+ });
+ }
+ }
+ }, number, countryIso);
+ if (!success) {
+ loadDataAndRender(views);
+ }
+ }
+
+ private void loadDataAndRender(CallLogListItemViewHolder views) {
+ int position = views.getAdapterPosition();
Cursor c = (Cursor) getItem(position);
if (c == null) {
return;
@@ -547,7 +576,6 @@ public class CallLogAdapter extends GroupingListAdapter
details.contactUserType = info.userType;
}
- final CallLogListItemViewHolder views = (CallLogListItemViewHolder) viewHolder;
views.info = info;
views.rowId = c.getLong(CallLogQuery.ID);
// Store values used when the actions ViewStub is inflated on expansion.
@@ -592,19 +620,22 @@ public class CallLogAdapter extends GroupingListAdapter
views.voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
}
- mCallLogListItemHelper.setPhoneCallDetails(views, details);
+ // Reversely pass spam information from views since details is not constructed when spam
+ // information comes back. This is used to render phone call details.
+ details.isSpam = views.isSpam;
+ render(views, details);
+ }
+ private void render(CallLogListItemViewHolder views, PhoneCallDetails details) {
+ mCallLogListItemHelper.setPhoneCallDetails(views, details);
if (mCurrentlyExpandedRowId == views.rowId) {
// In case ViewHolders were added/removed, update the expanded position if the rowIds
// match so that we can restore the correct expanded state on rebind.
- mCurrentlyExpandedPosition = position;
+ mCurrentlyExpandedPosition = views.getAdapterPosition();
views.showActions(true);
} else {
views.showActions(false);
}
- views.updatePhoto();
-
- mCallLogListItemHelper.setPhoneCallDetails(views, details);
}
private String getPreferredDisplayName(ContactInfo contactInfo) {
@@ -644,6 +675,16 @@ public class CallLogAdapter extends GroupingListAdapter
}
@Override
+ public long getItemId(int position) {
+ Cursor cursor = (Cursor) getItem(position);
+ if (cursor != null) {
+ return cursor.getLong(CallLogQuery.ID);
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
public int getGroupSize(int position) {
return super.getGroupSize(position - (mShowVoicemailPromoCard ? 1 : 0));
}
diff --git a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
index 34b2f0ea9..b95d58e26 100644
--- a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
+++ b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
@@ -17,6 +17,7 @@
package com.android.dialer.calllog;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -58,7 +59,8 @@ public class CallLogAsyncTaskUtil {
MARK_VOICEMAIL_READ,
MARK_CALL_READ,
GET_CALL_DETAILS,
- UPDATE_DURATION
+ UPDATE_DURATION,
+ GET_NUMBER_IN_CALL_HISTORY
}
private static final class CallDetailQuery {
@@ -122,6 +124,10 @@ public class CallLogAsyncTaskUtil {
void onGetCallDetails(PhoneCallDetails[] details);
}
+ public interface OnGetNumberInCallHistoryListener {
+ void onComplete(boolean inCallHistory);
+ }
+
public interface OnCallLogQueryFinishedListener {
void onQueryFinished(boolean hasEntry);
}
@@ -456,6 +462,42 @@ public class CallLogAsyncTaskUtil {
});
}
+ /**
+ * Checks if the number is in the call history.
+ */
+ public static void getNumberInCallHistory(
+ final Context context,
+ final String number,
+ final OnGetNumberInCallHistoryListener listener) {
+ Preconditions.checkNotNull(listener);
+ if (!PermissionsUtil.hasPhonePermissions(context)) {
+ return;
+ }
+ if (sAsyncTaskExecutor == null) {
+ initTaskExecutor();
+ }
+
+ sAsyncTaskExecutor.submit(Tasks.GET_NUMBER_IN_CALL_HISTORY,
+ new AsyncTask<Void, Void, Boolean>() {
+ @Override
+ public Boolean doInBackground(Void... params) {
+ try (Cursor cursor = context.getContentResolver().query(
+ TelecomUtil.getCallLogUri(context),
+ new String[] {CallLog.Calls._ID},
+ CallLog.Calls.NUMBER + " = ?",
+ new String[] {number},
+ null)) {
+ return cursor != null && cursor.getCount() > 0;
+ }
+ }
+
+ @Override
+ public void onPostExecute(Boolean inCallHistory) {
+ listener.onComplete(inCallHistory);
+ }
+ });
+ }
+
@VisibleForTesting
public static void resetForTest() {
sAsyncTaskExecutor = null;
diff --git a/src/com/android/dialer/calllog/CallLogListItemHelper.java b/src/com/android/dialer/calllog/CallLogListItemHelper.java
index 07e2bb425..18b6ff5d3 100644
--- a/src/com/android/dialer/calllog/CallLogListItemHelper.java
+++ b/src/com/android/dialer/calllog/CallLogListItemHelper.java
@@ -82,6 +82,8 @@ import com.android.dialer.calllog.calllogcache.CallLogCache;
// Cache country iso. Used for number filtering.
views.countryIso = details.countryIso;
+
+ views.updatePhoto();
}
/**
diff --git a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
index baf2e1ab5..858cc2102 100644
--- a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
+++ b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
@@ -54,18 +54,14 @@ import com.android.dialer.calllog.calllogcache.CallLogCache;
import com.android.dialer.compat.FilteredNumberCompat;
import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
import com.android.dialer.filterednumber.BlockNumberDialogFragment;
+import com.android.dialer.filterednumber.BlockedNumbersMigrator;
import com.android.dialer.filterednumber.FilteredNumbersUtil;
import com.android.dialer.logging.Logger;
import com.android.dialer.logging.ScreenEvent;
-import com.android.dialer.service.ExtendedBlockingButtonRenderer;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.util.PhoneNumberUtil;
import com.android.dialer.voicemail.VoicemailPlaybackLayout;
import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
-import com.android.dialerbind.ObjectFactory;
-import com.google.common.collect.Lists;
-
-import java.util.List;
/**
* This is an object containing references to views contained by the call log list item. This
@@ -77,6 +73,15 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener, MenuItem.OnMenuItemClickListener,
View.OnCreateContextMenuListener {
+ public interface OnClickListener {
+ void onBlockReportSpam(
+ String displayNumber, String number, String countryIso, int callType);
+ void onBlock(String displayNumber, String number, String countryIso, int callType);
+ void onUnblock(String displayNumber, String number, String countryIso, Integer blockId,
+ boolean isSpam, int callType);
+ void onReportNotSpam(String displayNumber, String number, String countryIso, int callType);
+ }
+
/** The root view of the call log list item */
public final View rootView;
/** The quick contact badge for the contact. */
@@ -101,6 +106,10 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
public View createNewContactButtonView;
public View addToExistingContactButtonView;
public View sendMessageView;
+ public View blockReportView;
+ public View blockView;
+ public View unblockView;
+ public View reportNotSpamView;
public View detailsButtonView;
public View callWithNoteButtonView;
public ImageView workIconView;
@@ -198,9 +207,14 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
public ContactInfo info;
/**
- * Whether the current log entry is a blocked number or not. Used in updatePhoto()
+ * Whether spam feature is enabled, which affects UI.
+ */
+ public boolean isSpamFeatureEnabled;
+
+ /**
+ * Whether the current log entry is a spam number or not.
*/
- public boolean isBlocked;
+ public boolean isSpam;
/**
* Whether this is the archive tab or not.
@@ -212,19 +226,18 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
private final CallLogListItemHelper mCallLogListItemHelper;
private final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
+ private final OnClickListener mBlockReportListener;
private final BlockNumberDialogFragment.Callback mFilteredNumberDialogCallback;
private final int mPhotoSize;
- private ViewStub mExtendedBlockingViewStub;
- private final ExtendedBlockingButtonRenderer mExtendedBlockingButtonRenderer;
private View.OnClickListener mExpandCollapseListener;
private boolean mVoicemailPrimaryActionButtonClicked;
private CallLogListItemViewHolder(
Context context,
- ExtendedBlockingButtonRenderer.Listener eventListener,
+ OnClickListener blockReportListener,
View.OnClickListener expandCollapseListener,
CallLogCache callLogCache,
CallLogListItemHelper callLogListItemHelper,
@@ -248,6 +261,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
mFilteredNumberAsyncQueryHandler = filteredNumberAsyncQueryHandler;
mFilteredNumberDialogCallback = filteredNumberDialogCallback;
+ mBlockReportListener = blockReportListener;
this.rootView = rootView;
this.quickContactView = quickContactView;
@@ -272,14 +286,12 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
primaryActionButtonView.setOnClickListener(this);
primaryActionView.setOnClickListener(mExpandCollapseListener);
primaryActionView.setOnCreateContextMenuListener(this);
- mExtendedBlockingButtonRenderer =
- ObjectFactory.newExtendedBlockingButtonRenderer(mContext, eventListener);
}
public static CallLogListItemViewHolder create(
View view,
Context context,
- ExtendedBlockingButtonRenderer.Listener eventListener,
+ OnClickListener blockReportListener,
View.OnClickListener expandCollapseListener,
CallLogCache callLogCache,
CallLogListItemHelper callLogListItemHelper,
@@ -290,7 +302,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
return new CallLogListItemViewHolder(
context,
- eventListener,
+ blockReportListener,
expandCollapseListener,
callLogCache,
callLogListItemHelper,
@@ -428,14 +440,23 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
sendMessageView = actionsView.findViewById(R.id.send_message_action);
sendMessageView.setOnClickListener(this);
+ blockReportView = actionsView.findViewById(R.id.block_report_action);
+ blockReportView.setOnClickListener(this);
+
+ blockView = actionsView.findViewById(R.id.block_action);
+ blockView.setOnClickListener(this);
+
+ unblockView = actionsView.findViewById(R.id.unblock_action);
+ unblockView.setOnClickListener(this);
+
+ reportNotSpamView = actionsView.findViewById(R.id.report_not_spam_action);
+ reportNotSpamView.setOnClickListener(this);
+
detailsButtonView = actionsView.findViewById(R.id.details_action);
detailsButtonView.setOnClickListener(this);
callWithNoteButtonView = actionsView.findViewById(R.id.call_with_note_action);
callWithNoteButtonView.setOnClickListener(this);
-
- mExtendedBlockingViewStub =
- (ViewStub) actionsView.findViewById(R.id.extended_blocking_actions_container);
}
bindActionButtons();
@@ -568,32 +589,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
callWithNoteButtonView.setVisibility(
supportsCallSubject && !isVoicemailNumber ? View.VISIBLE : View.GONE);
- if(mExtendedBlockingButtonRenderer != null){
- List<View> completeLogListItems = Lists.newArrayList(
- createNewContactButtonView,
- addToExistingContactButtonView,
- sendMessageView,
- callButtonView,
- callWithNoteButtonView,
- detailsButtonView,
- voicemailPlaybackView);
-
- List<View> blockedNumberVisibleViews = Lists.newArrayList(detailsButtonView);
- List<View> extendedBlockingVisibleViews = Lists.newArrayList(detailsButtonView);
-
- ExtendedBlockingButtonRenderer.ViewHolderInfo viewHolderInfo =
- new ExtendedBlockingButtonRenderer.ViewHolderInfo(
- completeLogListItems,
- extendedBlockingVisibleViews,
- blockedNumberVisibleViews,
- number,
- countryIso,
- nameOrNumber.toString(),
- displayNumber);
- mExtendedBlockingButtonRenderer.setViewHolderInfo(viewHolderInfo);
-
- mExtendedBlockingButtonRenderer.render(mExtendedBlockingViewStub);
- }
+ updateBlockReportActions();
}
/**
@@ -635,6 +631,11 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
}
public void updatePhoto() {
+ if (isSpamFeatureEnabled && isSpam) {
+ quickContactView.setImageDrawable(
+ mContext.getDrawable(R.drawable.blocked_contact));
+ return;
+ }
quickContactView.assignContactUri(info.lookupUri);
final boolean isVoicemail = mCallLogCache.isVoicemailNumber(accountHandle, number);
@@ -658,14 +659,6 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
ContactPhotoManager.getInstance(mContext).loadThumbnail(quickContactView, info.photoId,
false /* darkTheme */, true /* isCircular */, request);
}
-
- if (mExtendedBlockingButtonRenderer != null) {
- mExtendedBlockingButtonRenderer.updatePhotoAndLabelIfNecessary(
- number,
- countryIso,
- quickContactView,
- phoneCallDetailsViews.callLocationAndDate);
- }
}
@Override
@@ -686,6 +679,26 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
view in dialog. */
numberType, /* phone number type (e.g. mobile) in second line of contact view */
accountHandle);
+ } else if (view.getId() == R.id.block_report_action) {
+ maybeShowBlockNumberMigrationDialog(new BlockedNumbersMigrator.Listener() {
+ @Override
+ public void onComplete() {
+ mBlockReportListener.onBlockReportSpam(
+ displayNumber, number, countryIso, callType);
+ }
+ });
+ } else if (view.getId() == R.id.block_action) {
+ maybeShowBlockNumberMigrationDialog(new BlockedNumbersMigrator.Listener() {
+ @Override
+ public void onComplete() {
+ mBlockReportListener.onBlock(displayNumber, number, countryIso, callType);
+ }
+ });
+ } else if (view.getId() == R.id.unblock_action) {
+ mBlockReportListener.onUnblock(
+ displayNumber, number, countryIso, blockId, isSpam, callType);
+ } else if (view.getId() == R.id.report_not_spam_action) {
+ mBlockReportListener.onReportNotSpam(displayNumber, number, countryIso, callType);
} else {
final IntentProvider intentProvider = (IntentProvider) view.getTag();
if (intentProvider != null) {
@@ -698,6 +711,14 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
}
}
+ private void maybeShowBlockNumberMigrationDialog(BlockedNumbersMigrator.Listener listener) {
+ if (!FilteredNumberCompat.maybeShowBlockNumberMigrationDialog(
+ mContext.getContentResolver(),
+ ((Activity) mContext).getFragmentManager(), listener)) {
+ listener.onComplete();
+ }
+ }
+
@NeededForTesting
public static CallLogListItemViewHolder createForTest(Context context) {
Resources resources = context.getResources();
@@ -729,4 +750,27 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
viewHolder.workIconView = new ImageButton(context);
return viewHolder;
}
+
+ private void updateBlockReportActions() {
+ if (!isSpamFeatureEnabled) {
+ return;
+ }
+ // Set block/spam actions.
+ blockReportView.setVisibility(View.GONE);
+ blockView.setVisibility(View.GONE);
+ unblockView.setVisibility(View.GONE);
+ reportNotSpamView.setVisibility(View.GONE);
+ boolean isBlocked = blockId != null;
+ if (isBlocked) {
+ unblockView.setVisibility(View.VISIBLE);
+ } else {
+ if (isSpam) {
+ blockView.setVisibility(View.VISIBLE);
+ reportNotSpamView.setVisibility(View.VISIBLE);
+ } else {
+ blockReportView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ }
} \ No newline at end of file
diff --git a/src/com/android/dialer/calllog/ContactInfoHelper.java b/src/com/android/dialer/calllog/ContactInfoHelper.java
index b0ef0abf4..d9898ab94 100644
--- a/src/com/android/dialer/calllog/ContactInfoHelper.java
+++ b/src/com/android/dialer/calllog/ContactInfoHelper.java
@@ -14,6 +14,8 @@
package com.android.dialer.calllog;
+import com.google.common.primitives.Longs;
+
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
@@ -33,6 +35,7 @@ import android.util.Log;
import com.android.contacts.common.ContactsUtils;
import com.android.contacts.common.ContactsUtils.UserType;
import com.android.contacts.common.compat.CompatUtils;
+import com.android.contacts.common.compat.DirectoryCompat;
import com.android.contacts.common.util.Constants;
import com.android.contacts.common.util.PermissionsUtil;
import com.android.contacts.common.util.PhoneNumberHelper;
@@ -163,6 +166,9 @@ public class ContactInfoHelper {
return ContactInfo.EMPTY;
}
+ final String directory = uri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
+ final Long directoryId = directory == null ? null : Longs.tryParse(directory);
+
Cursor phoneLookupCursor = null;
try {
String[] projection = PhoneQuery.getPhoneLookupProjection(uri);
@@ -183,7 +189,7 @@ public class ContactInfoHelper {
String lookupKey = phoneLookupCursor.getString(PhoneQuery.LOOKUP_KEY);
ContactInfo contactInfo = createPhoneLookupContactInfo(phoneLookupCursor, lookupKey);
contactInfo.nameAlternative = lookUpDisplayNameAlternative(mContext, lookupKey,
- contactInfo.userType);
+ contactInfo.userType, directoryId);
return contactInfo;
} finally {
phoneLookupCursor.close();
@@ -210,11 +216,24 @@ public class ContactInfoHelper {
}
public static String lookUpDisplayNameAlternative(Context context, String lookupKey,
- @UserType long userType) {
+ @UserType long userType, @Nullable Long directoryId) {
// Query {@link Contacts#CONTENT_LOOKUP_URI} directly with work lookup key is not allowed.
if (lookupKey == null || userType == ContactsUtils.USER_TYPE_WORK) {
return null;
}
+
+ if (directoryId != null) {
+ // Query {@link Contacts#CONTENT_LOOKUP_URI} with work lookup key is not allowed.
+ if (DirectoryCompat.isEnterpriseDirectoryId(directoryId)) {
+ return null;
+ }
+
+ // Skip this to avoid an extra remote network call for alternative name
+ if (DirectoryCompat.isRemoteDirectoryId(directoryId)) {
+ return null;
+ }
+ }
+
final Uri uri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);
Cursor cursor = null;
try {
@@ -226,6 +245,7 @@ public class ContactInfoHelper {
}
} catch (IllegalArgumentException e) {
// Avoid dialer crash when lookup key is not valid
+ Log.e(TAG, "IllegalArgumentException in lookUpDisplayNameAlternative", e);
} finally {
if (cursor != null) {
cursor.close();
diff --git a/src/com/android/dialer/calllog/MissedCallNotifier.java b/src/com/android/dialer/calllog/MissedCallNotifier.java
index 20fc5d551..3afc112b2 100644
--- a/src/com/android/dialer/calllog/MissedCallNotifier.java
+++ b/src/com/android/dialer/calllog/MissedCallNotifier.java
@@ -96,6 +96,7 @@ public class MissedCallNotifier {
boolean useCallLog = newCalls != null && newCalls.size() == count;
NewCall newestCall = useCallLog ? newCalls.get(0) : null;
long timeMs = useCallLog ? newestCall.dateMs : System.currentTimeMillis();
+ String missedNumber = useCallLog ? newestCall.number : number;
Notification.Builder builder = new Notification.Builder(mContext);
// Display the first line of the notification:
@@ -104,7 +105,7 @@ public class MissedCallNotifier {
if (count == 1) {
//TODO: look up caller ID that is not in contacts.
ContactInfo contactInfo = CallLogNotificationsHelper.getInstance(mContext)
- .getContactInfo(useCallLog ? newestCall.number : number,
+ .getContactInfo(missedNumber,
useCallLog ? newestCall.numberPresentation
: Calls.PRESENTATION_ALLOWED,
useCallLog ? newestCall.countryIso : null);
@@ -156,17 +157,17 @@ public class MissedCallNotifier {
// Add additional actions when there is only 1 missed call and the user isn't locked
if (UserManagerCompat.isUserUnlocked(mContext) && count == 1) {
- if (!TextUtils.isEmpty(number)
+ if (!TextUtils.isEmpty(missedNumber)
&& !TextUtils.equals(
- number, mContext.getString(R.string.handle_restricted))) {
+ missedNumber, mContext.getString(R.string.handle_restricted))) {
builder.addAction(R.drawable.ic_phone_24dp,
mContext.getString(R.string.notification_missedCall_call_back),
- createCallBackPendingIntent(number));
+ createCallBackPendingIntent(missedNumber));
- if (!PhoneNumberHelper.isUriNumber(number)) {
+ if (!PhoneNumberHelper.isUriNumber(missedNumber)) {
builder.addAction(R.drawable.ic_message_24dp,
mContext.getString(R.string.notification_missedCall_message),
- createSendSmsFromNotificationPendingIntent(number));
+ createSendSmsFromNotificationPendingIntent(missedNumber));
}
}
}
diff --git a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java b/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
index 4f1c45503..53121614c 100644
--- a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
+++ b/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
@@ -324,6 +324,11 @@ public class PhoneCallDetailsHelper {
/** Sets the call count, date, and if it is a voicemail, sets the duration. */
private void setDetailText(PhoneCallDetailsViews views, Integer callCount,
PhoneCallDetails details) {
+ if (details.isSpam) {
+ views.callLocationAndDate.setText(
+ mContext.getString(R.string.spam_number_call_log_label));
+ return;
+ }
// Combine the count (if present) and the date.
CharSequence dateText = getCallLocationAndDate(details);
final CharSequence text;
@@ -341,7 +346,6 @@ public class PhoneCallDetailsHelper {
} else {
views.callLocationAndDate.setText(text);
}
-
}
private String getVoicemailDuration(PhoneCallDetails details) {
diff --git a/src/com/android/dialer/compat/FilteredNumberCompat.java b/src/com/android/dialer/compat/FilteredNumberCompat.java
index 008782d86..8d8e9a2a5 100644
--- a/src/com/android/dialer/compat/FilteredNumberCompat.java
+++ b/src/com/android/dialer/compat/FilteredNumberCompat.java
@@ -247,6 +247,27 @@ public class FilteredNumberCompat {
}
/**
+ * Shows block number migration dialog if necessary.
+ *
+ * @param fragmentManager The {@link FragmentManager} used to show fragments.
+ * @param listener The {@link BlockedNumbersMigrator.Listener} to call when migration is
+ * complete.
+ * @return boolean True if migration dialog is shown.
+ */
+ public static boolean maybeShowBlockNumberMigrationDialog(
+ ContentResolver contentResolver, FragmentManager fragmentManager,
+ BlockedNumbersMigrator.Listener listener) {
+ if (shouldShowMigrationDialog(true)) {
+ Log.i(TAG, "maybeShowBlockNumberMigrationDialog - showing migration dialog");
+ MigrateBlockedNumbersDialogFragment
+ .newInstance(new BlockedNumbersMigrator(contentResolver), listener)
+ .show(fragmentManager, "MigrateBlockedNumbers");
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Shows the flow of {@link android.app.DialogFragment}s for blocking or unblocking numbers.
*
* @param blockId The id into the blocked numbers database.
diff --git a/src/com/android/dialer/filterednumber/FilteredNumbersUtil.java b/src/com/android/dialer/filterednumber/FilteredNumbersUtil.java
index 498b10a45..35d6f8d25 100644
--- a/src/com/android/dialer/filterednumber/FilteredNumbersUtil.java
+++ b/src/com/android/dialer/filterednumber/FilteredNumbersUtil.java
@@ -32,6 +32,7 @@ import android.text.TextUtils;
import android.widget.Toast;
import com.android.contacts.common.testing.NeededForTesting;
+import com.android.contacts.common.util.PermissionsUtil;
import com.android.dialer.R;
import com.android.dialer.compat.FilteredNumberCompat;
import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
@@ -124,7 +125,7 @@ public class FilteredNumbersUtil {
final AsyncTask task = new AsyncTask<Object, Void, Boolean>() {
@Override
public Boolean doInBackground(Object[] params) {
- if (context == null) {
+ if (context == null || !PermissionsUtil.hasContactsPermissions(context)) {
return false;
}
diff --git a/src/com/android/dialer/service/ExtendedBlockingButtonRenderer.java b/src/com/android/dialer/service/ExtendedBlockingButtonRenderer.java
deleted file mode 100644
index f8d5ea048..000000000
--- a/src/com/android/dialer/service/ExtendedBlockingButtonRenderer.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.dialer.service;
-
-import android.support.annotation.Nullable;
-import android.view.View;
-import android.view.ViewStub;
-import android.widget.QuickContactBadge;
-import android.widget.TextView;
-
-import java.util.List;
-
-/**
- * Interface responsible for rendering spam buttons.
- */
-public interface ExtendedBlockingButtonRenderer {
-
- final class ViewHolderInfo {
-
- public final List<View> completeListItemViews;
- public final List<View> extendedBlockedViews;
- public final List<View> blockedNumberViews;
- public final String phoneNumber;
- public final String countryIso;
- public final String nameOrNumber;
- public final String displayNumber;
-
- public ViewHolderInfo(
- /* All existing views amongst the list item actions, even if invisible */
- List<View> completeListItemViews,
- /* Views that should be seen if the number is in the blacklist */
- List<View> extendedBlockedViews,
- /* Views that should be seen if the number is in the extended blacklist */
- List<View> blockedNumberViews,
- String phoneNumber,
- String countryIso,
- String nameOrNumber,
- String displayNumber) {
-
- this.completeListItemViews = completeListItemViews;
- this.extendedBlockedViews = extendedBlockedViews;
- this.blockedNumberViews = blockedNumberViews;
- this.phoneNumber = phoneNumber;
- this.countryIso = countryIso;
- this.nameOrNumber = nameOrNumber;
- this.displayNumber = displayNumber;
- }
- }
-
- interface Listener {
- void onBlockedNumber(String number, @Nullable String countryIso);
- void onUnblockedNumber(String number, @Nullable String countryIso);
- }
-
- /**
- * Renders buttons for a phone number.
- */
- void render(ViewStub viewStub);
-
- void setViewHolderInfo(ViewHolderInfo info);
-
- /**
- * Updates the photo and label for the given phone number and country iso.
- *
- * @param number Phone number for which the rendering occurs.
- * @param countryIso Two-letter country code.
- * @param badge {@link QuickContactBadge} in which the photo should be rendered.
- * @param view Textview that will hold the new label.
- */
- void updatePhotoAndLabelIfNecessary(
- String number, String countryIso, QuickContactBadge badge, TextView view);
-}
diff --git a/src/com/android/dialer/service/ExtendedCallInfoService.java b/src/com/android/dialer/service/ExtendedCallInfoService.java
new file mode 100644
index 000000000..5481cc90d
--- /dev/null
+++ b/src/com/android/dialer/service/ExtendedCallInfoService.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dialer.service;
+
+import android.support.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Interface of service to get extended call information.
+ */
+public interface ExtendedCallInfoService {
+ /**
+ * All the possible locations that a user can report a number as spam or not spam.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({REPORTING_LOCATION_UNKNOWN, REPORTING_LOCATION_CALL_LOG_HISTORY,
+ REPORTING_LOCATION_FEEDBACK_PROMPT})
+ @interface ReportingLocation {}
+ int REPORTING_LOCATION_UNKNOWN = 0;
+ int REPORTING_LOCATION_CALL_LOG_HISTORY = 1;
+ int REPORTING_LOCATION_FEEDBACK_PROMPT = 2;
+
+ /**
+ * Interface for a callback to be invoked when data is fetched.
+ */
+ interface Listener {
+ /**
+ * Called when data is fetched.
+ * @param isSpam True if the call is spam.
+ */
+ void onComplete(boolean isSpam);
+ }
+
+ /**
+ * Gets extended call information.
+ * @param number The phone number of the call.
+ * @param countryIso The country ISO of the call.
+ * @param listener The callback to be invoked after {@code Info} is fetched.
+ */
+ void getExtendedCallInfo(String number, String countryIso, Listener listener);
+
+ /**
+ * Reports number as spam.
+ * @param number The number to be reported.
+ * @param countryIso The country ISO of the number.
+ * @param callType Whether the type of call is missed, voicemail, etc. Example of this is
+ * {@link android.provider.CallLog.Calls#VOICEMAIL_TYPE}.
+ * @param from Where in the dialer this was reported from.
+ * Must be one of {@link ReportingLocation}.
+ */
+ void reportSpam(String number, String countryIso, int callType, @ReportingLocation int from);
+
+ /**
+ * Reports number as not spam.
+ * @param number The number to be reported.
+ * @param countryIso The country ISO of the number.
+ * @param callType Whether the type of call is missed, voicemail, etc. Example of this is
+ * {@link android.provider.CallLog.Calls#VOICEMAIL_TYPE}.
+ * @param from Where in the dialer this was reported from.
+ * Must be one of {@link ReportingLocation}.
+ */
+ void reportNotSpam(String number, String countryIso, int callType, @ReportingLocation int from);
+}
diff --git a/src/com/android/dialer/util/BlockReportSpamDialogs.java b/src/com/android/dialer/util/BlockReportSpamDialogs.java
new file mode 100644
index 000000000..45b2f4228
--- /dev/null
+++ b/src/com/android/dialer/util/BlockReportSpamDialogs.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dialer.util;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+
+import com.android.dialer.R;
+
+/**
+ * Helper class for creating block/report dialog fragments.
+ */
+public class BlockReportSpamDialogs {
+ public static final String BLOCK_REPORT_SPAM_DIALOG_TAG = "BlockReportSpamDialog";
+ public static final String BLOCK_DIALOG_TAG = "BlockDialog";
+ public static final String UNBLOCK_DIALOG_TAG = "UnblockDialog";
+ public static final String NOT_SPAM_DIALOG_TAG = "NotSpamDialog";
+
+ /**
+ * Listener passed to block/report spam dialog for positive click in
+ * {@link BlockReportSpamDialogFragment}.
+ */
+ public interface OnSpamDialogClickListener {
+ /**
+ * Called when user clicks on positive button in block/report spam dialog.
+ * @param isSpamChecked Whether the spam checkbox is checked.
+ */
+ void onClick(boolean isSpamChecked);
+ }
+
+ /**
+ * Listener passed to all dialogs except the block/report spam dialog for positive click.
+ */
+ public interface OnConfirmListener {
+ /**
+ * Called when user clicks on positive button in the dialog.
+ */
+ void onClick();
+ }
+
+ /**
+ * Contains the common attributes between all block/unblock/report dialog fragments.
+ */
+ private static class CommonDialogsFragment extends DialogFragment {
+ /**
+ * The number to display in the dialog title.
+ */
+ protected String mDisplayNumber;
+
+ /**
+ * Called when dialog positive button is pressed.
+ */
+ protected OnConfirmListener mPositiveListener;
+
+ /**
+ * Called when dialog is dismissed.
+ */
+ @Nullable
+ protected DialogInterface.OnDismissListener mDismissListener;
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ if (mDismissListener != null) {
+ mDismissListener.onDismiss(dialog);
+ }
+ super.onDismiss(dialog);
+ }
+
+ @Override
+ public void onPause() {
+ // The dialog is dismissed onPause, i.e. rotation.
+ dismiss();
+ mDismissListener = null;
+ mPositiveListener = null;
+ mDisplayNumber = null;
+ super.onPause();
+ }
+ }
+
+ /**
+ * Dialog for block/report spam with the mark as spam checkbox.
+ */
+ public static class BlockReportSpamDialogFragment extends CommonDialogsFragment {
+ /**
+ * Called when dialog positive button is pressed.
+ */
+ private OnSpamDialogClickListener mPositiveListener;
+
+ /**
+ * Whether the mark as spam checkbox is checked before displaying the dialog.
+ */
+ private boolean mSpamChecked;
+
+ public static DialogFragment newInstance(String displayNumber,
+ boolean spamChecked,
+ OnSpamDialogClickListener positiveListener,
+ @Nullable DialogInterface.OnDismissListener
+ dismissListener) {
+ BlockReportSpamDialogFragment fragment = new BlockReportSpamDialogFragment();
+ fragment.mSpamChecked = spamChecked;
+ fragment.mDisplayNumber = displayNumber;
+ fragment.mPositiveListener = positiveListener;
+ fragment.mDismissListener = dismissListener;
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+ View dialogView = View.inflate(getActivity(), R.layout.block_report_spam_dialog, null);
+ final CheckBox isSpamCheckbox =
+ (CheckBox) dialogView.findViewById(R.id.report_number_as_spam_action);
+ // Listen for changes on the checkbox and update if orientation changes
+ isSpamCheckbox.setChecked(mSpamChecked);
+ isSpamCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ mSpamChecked = isChecked;
+ }
+ });
+
+ AlertDialog.Builder alertDialogBuilder = createDialogBuilder(getActivity(), this);
+ Dialog dialog = alertDialogBuilder
+ .setView(dialogView)
+ .setTitle(getString(R.string.block_report_number_alert_title, mDisplayNumber))
+ .setPositiveButton(R.string.block_number_ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ mPositiveListener.onClick(isSpamCheckbox.isChecked());
+ }
+ }).create();
+ dialog.setCanceledOnTouchOutside(true);
+ return dialog;
+ }
+ }
+
+ /**
+ * Dialog for blocking a number.
+ */
+ public static class BlockDialogFragment extends CommonDialogsFragment {
+ public static DialogFragment newInstance(String displayNumber,
+ OnConfirmListener positiveListener,
+ @Nullable DialogInterface.OnDismissListener
+ dismissListener) {
+ BlockDialogFragment fragment = new BlockDialogFragment();
+ fragment.mDisplayNumber = displayNumber;
+ fragment.mPositiveListener = positiveListener;
+ fragment.mDismissListener = dismissListener;
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+ // Return the newly created dialog
+ AlertDialog.Builder alertDialogBuilder = createDialogBuilder(getActivity(), this);
+ Dialog dialog = alertDialogBuilder
+ .setTitle(getString(R.string.block_report_number_alert_title, mDisplayNumber))
+ .setPositiveButton(R.string.block_number_ok,
+ createGenericOnClickListener(this, mPositiveListener))
+ .create();
+ dialog.setCanceledOnTouchOutside(true);
+ return dialog;
+ }
+ }
+
+ /**
+ * Dialog for unblocking a number.
+ */
+ public static class UnblockDialogFragment extends CommonDialogsFragment {
+ /**
+ * Whether or not the number is spam.
+ */
+ private boolean mIsSpam;
+
+ public static DialogFragment newInstance(String displayNumber,
+ boolean isSpam,
+ OnConfirmListener positiveListener,
+ @Nullable DialogInterface.OnDismissListener
+ dismissListener) {
+ UnblockDialogFragment fragment = new UnblockDialogFragment();
+ fragment.mDisplayNumber = displayNumber;
+ fragment.mIsSpam = isSpam;
+ fragment.mPositiveListener = positiveListener;
+ fragment.mDismissListener = dismissListener;
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+ // Return the newly created dialog
+ AlertDialog.Builder alertDialogBuilder = createDialogBuilder(getActivity(), this);
+ if (mIsSpam) {
+ alertDialogBuilder.setMessage(R.string.unblock_number_alert_details);
+ }
+ Dialog dialog = alertDialogBuilder
+ .setTitle(getString(R.string.unblock_report_number_alert_title, mDisplayNumber))
+ .setPositiveButton(R.string.unblock_number_ok,
+ createGenericOnClickListener(this, mPositiveListener))
+ .create();
+ dialog.setCanceledOnTouchOutside(true);
+ return dialog;
+ }
+ }
+
+ /**
+ * Dialog for reporting a number as not spam.
+ */
+ public static class ReportNotSpamDialogFragment extends CommonDialogsFragment {
+ public static DialogFragment newInstance(String displayNumber,
+ OnConfirmListener positiveListener,
+ @Nullable DialogInterface.OnDismissListener
+ dismissListener) {
+ ReportNotSpamDialogFragment fragment = new ReportNotSpamDialogFragment();
+ fragment.mDisplayNumber = displayNumber;
+ fragment.mPositiveListener = positiveListener;
+ fragment.mDismissListener = dismissListener;
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreateDialog(savedInstanceState);
+ // Return the newly created dialog
+ AlertDialog.Builder alertDialogBuilder = createDialogBuilder(getActivity(), this);
+ Dialog dialog = alertDialogBuilder
+ .setTitle(getString(R.string.report_not_spam_alert_title, mDisplayNumber))
+ .setMessage(R.string.report_not_spam_alert_details)
+ .setPositiveButton(R.string.report_not_spam_alert_button,
+ createGenericOnClickListener(this, mPositiveListener))
+ .create();
+ dialog.setCanceledOnTouchOutside(true);
+ return dialog;
+ }
+ }
+
+ /**
+ * Creates a dialog with the default cancel button listener (dismisses dialog).
+ */
+ private static AlertDialog.Builder createDialogBuilder(Activity activity,
+ final DialogFragment fragment) {
+ return new AlertDialog.Builder(activity)
+ .setCancelable(true)
+ .setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ fragment.dismiss();
+ }
+ });
+ }
+
+ /**
+ * Creates a generic click listener which dismisses the fragment and then calls the actual
+ * listener.
+ */
+ private static DialogInterface.OnClickListener createGenericOnClickListener(
+ final DialogFragment fragment,
+ final OnConfirmListener listener) {
+ return new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ fragment.dismiss();
+ listener.onClick();
+ }
+ };
+ }
+}
diff --git a/src/com/android/dialer/util/IntentUtil.java b/src/com/android/dialer/util/IntentUtil.java
index 5a4a80bb1..581e10da4 100644
--- a/src/com/android/dialer/util/IntentUtil.java
+++ b/src/com/android/dialer/util/IntentUtil.java
@@ -19,6 +19,7 @@ package com.android.dialer.util;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.os.SystemClock;
import android.provider.ContactsContract;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -37,6 +38,8 @@ public class IntentUtil {
public static final String EXTRA_CALL_INITIATION_TYPE
= "com.android.dialer.EXTRA_CALL_INITIATION_TYPE";
+ public static final String EXTRA_CALL_CREATED_TIME_MILLIS =
+ "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
public static class CallIntentBuilder {
private Uri mUri;
@@ -91,6 +94,7 @@ public class IntentUtil {
intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
final Bundle b = new Bundle();
+ b.putLong(EXTRA_CALL_CREATED_TIME_MILLIS, SystemClock.elapsedRealtime());
b.putInt(EXTRA_CALL_INITIATION_TYPE, callIntiationType);
intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, b);