Merge "Send notification when permission granted in search" into ub-contactsdialer-a-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 32d88cc..1faab2a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -17,8 +17,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.dialer"
coreApp="true"
- android:versionCode="20210"
- android:versionName="2.21">
+ android:versionCode="20300"
+ android:versionName="2.3">
<uses-sdk
android:minSdkVersion="23"
diff --git a/res/drawable/tab_contacts.xml b/res/drawable/tab_contacts.xml
deleted file mode 100644
index ed3f86b..0000000
--- a/res/drawable/tab_contacts.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/tab_ripple_color">
-
- <item>
- <bitmap android:src="@drawable/ic_people_24dp"
- android:gravity="center" />
- </item>
-
- <item android:id="@android:id/mask">
- <color android:color="@android:color/white" />
- </item>
-
-</ripple>
diff --git a/res/drawable/tab_history.xml b/res/drawable/tab_history.xml
deleted file mode 100644
index b1a5318..0000000
--- a/res/drawable/tab_history.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/tab_ripple_color">
-
- <item>
- <bitmap android:src="@drawable/ic_schedule_24dp"
- android:gravity="center" />
- </item>
-
- <item android:id="@android:id/mask">
- <color android:color="@android:color/white" />
- </item>
-
-</ripple>
diff --git a/res/drawable/tab_speed_dial.xml b/res/drawable/tab_speed_dial.xml
deleted file mode 100644
index c9c4820..0000000
--- a/res/drawable/tab_speed_dial.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/tab_ripple_color">
-
- <item>
- <bitmap android:src="@drawable/ic_grade_24dp"
- android:gravity="center" />
- </item>
-
- <item android:id="@android:id/mask">
- <color android:color="@android:color/white" />
- </item>
-
-</ripple>
diff --git a/res/drawable/tab_voicemail.xml b/res/drawable/tab_voicemail.xml
deleted file mode 100644
index 568891b..0000000
--- a/res/drawable/tab_voicemail.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/tab_ripple_color">
-
- <item>
- <bitmap android:src="@drawable/ic_voicemail_24dp"
- android:gravity="center" />
- </item>
-
- <item android:id="@android:id/mask">
- <color android:color="@android:color/white" />
- </item>
-
-</ripple>
diff --git a/res/layout/voicemail_promo_card.xml b/res/layout/voicemail_promo_card.xml
index 103fa30..891f096 100644
--- a/res/layout/voicemail_promo_card.xml
+++ b/res/layout/voicemail_promo_card.xml
@@ -79,20 +79,20 @@
android:gravity="end">
<TextView
- android:id="@+id/settings_action"
+ android:id="@+id/secondary_action"
style="@style/PromoCardActionStyle"
android:background="?android:attr/selectableItemBackground"
android:text="@string/visual_voicemail_settings"
android:nextFocusLeft="@+id/promo_card"
- android:nextFocusRight="@+id/ok_action"
+ android:nextFocusRight="@+id/primary_action"
android:paddingEnd="@dimen/promo_card_action_between_padding"/>
<TextView
- android:id="@+id/ok_action"
+ android:id="@+id/primary_action"
style="@style/PromoCardActionStyle"
android:background="?android:attr/selectableItemBackground"
android:text="@android:string/ok"
- android:nextFocusLeft="@+id/settings_action"
+ android:nextFocusLeft="@+id/secondary_action"
android:nextFocusRight="@+id/promo_card"/>
</LinearLayout>
</LinearLayout>
diff --git a/res/menu/call_details_options.xml b/res/menu/call_details_options.xml
index 84cc2c9..0e9e5c9 100644
--- a/res/menu/call_details_options.xml
+++ b/res/menu/call_details_options.xml
@@ -13,7 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/call_details_menu">
<item android:id="@+id/menu_trash"
android:icon="@drawable/ic_delete_24dp"
diff --git a/res/values/ids.xml b/res/values/ids.xml
index cb2025a..0034fe3 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -18,5 +18,6 @@
<item type="id" name="context_menu_copy_to_clipboard" />
<item type="id" name="context_menu_copy_transcript_to_clipboard" />
<item type="id" name="context_menu_edit_before_call" />
+ <item type="id" name="context_menu_block_number" />
<item type="id" name="settings_header_sounds_and_vibration" />
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1ce6ab6..7bd88ba 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -52,6 +52,24 @@
<!-- Option displayed in context menu to copy long pressed voicemail transcription to clipboard [CHAR LIMIT=64] -->
<string name="copy_transcript_text">Copy transcription to clipboard</string>
+ <!-- Menu item used to block a number from the call log [CHAR LIMIT=64] -->
+ <string name="call_log_block_number">Block number</string>
+
+ <!-- Text for snackbar to undo blocking a number. [CHAR LIMIT=64] -->
+ <string name="snackbar_number_blocked">
+ <xliff:g id="number" example="(555) 555-5555">%1$s</xliff:g> added to block list</string>
+
+ <!-- Menu item used to unblock a number from the call log [CHAR LIMIT=64]-->
+ <string name="call_log_unblock_number">Unblock number</string>
+
+ <!-- Text for snackbar to undo unblocking a number. [CHAR LIMIT=64] -->
+ <string name="snackbar_number_unblocked">
+ <xliff:g id="number" example="(555) 555-5555">%1$s</xliff:g>
+ removed from block list</string>
+
+ <!-- Text for undo button in snackbar for blocking/unblocking number. [CHAR LIMIT=10] -->
+ <string name="block_number_undo">UNDO</string>
+
<!-- Menu item used to copy a number from the call log to the dialer so it can be edited before calling it -->
<string name="call_log_edit_number_before_call">Edit number before call</string>
@@ -490,6 +508,20 @@
[CHAR LIMIT=30] -->
<string name="call_log_voicemail_title">Voicemail</string>
+ <!-- Confirmation dialog for blocking a number. [CHAR LIMIT=NONE] -->
+ <string name="blockNumberConfirmation">Add
+ <xliff:g id="number" example="(555) 555-5555">%1$s</xliff:g> to your block list?</string>
+
+ <!-- Block number alert dialog button [CHAR LIMIT=32] -->
+ <string name="blockNumberOk">Block number</string>
+
+ <!-- Confirmation dialog for unblocking a number. [CHAR LIMIT=NONE] -->
+ <string name="unblockNumberConfirmation">Remove
+ <xliff:g id="number" example="(555) 555-5555">%1$s</xliff:g> from your block list?</string>
+
+ <!-- Unblock number alert dialog button [CHAR LIMIT=32] -->
+ <string name="unblockNumberOk">Unblock number</string>
+
<!-- Accessibility text for the tab showing recent and favorite contacts who can be called.
[CHAR LIMIT=40] -->
<string name="tab_speed_dial">Speed dial</string>
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index ae1a160..4ade04a 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -693,6 +693,14 @@
}
/**
+ * Update the number of unread voicemails (potentially other tabs) displayed next to the tab
+ * icon.
+ */
+ public void updateTabUnreadCounts() {
+ mListsFragment.updateTabUnreadCounts();
+ }
+
+ /**
* Initiates a fragment transaction to show the dialpad fragment. Animations and other visual
* updates are handled by a callback which is invoked after the dialpad fragment is shown.
* @see #onDialpadShown
@@ -907,11 +915,11 @@
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 4593e5e..90985af 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -22,12 +22,10 @@
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.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;
@@ -79,11 +67,9 @@
private static final int NO_EXPANDED_LIST_ITEM = -1;
private static final int VOICEMAIL_PROMO_CARD_POSITION = 0;
- /**
- * View type for voicemail promo card. Note: Numbering starts at 20 to avoid collision
- * with {@link com.android.common.widget.GroupingListAdapter#ITEM_TYPE_IN_GROUP}.
- */
- private static final int VIEW_TYPE_VOICEMAIL_PROMO_CARD = 20;
+
+ protected static final int VIEW_TYPE_NORMAL = 0;
+ private static final int VIEW_TYPE_VOICEMAIL_PROMO_CARD = 1;
/**
* The key for the show voicemail promo card preference which will determine whether the promo
@@ -96,6 +82,7 @@
private final ContactInfoHelper mContactInfoHelper;
protected final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
private final CallFetcher mCallFetcher;
+ private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
protected ContactInfoCache mContactInfoCache;
@@ -195,89 +182,6 @@
}
};
- /**
- * 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 +255,8 @@
mCallLogGroupBuilder = new CallLogGroupBuilder(this);
mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
maybeShowVoicemailPromoCard();
+ mFilteredNumberAsyncQueryHandler =
+ new FilteredNumberAsyncQueryHandler(mContext.getContentResolver());
}
public void onSaveInstanceState(Bundle outState) {
@@ -439,12 +345,12 @@
mExpandCollapseListener,
mTelecomCallLogCache,
mCallLogListItemHelper,
- mVoicemailPlaybackPresenter);
+ mVoicemailPlaybackPresenter,
+ mFilteredNumberAsyncQueryHandler);
viewHolder.callLogEntryView.setTag(viewHolder);
viewHolder.callLogEntryView.setAccessibilityDelegate(mAccessibilityDelegate);
- viewHolder.primaryActionView.setOnCreateContextMenuListener(mOnCreateContextMenuListener);
viewHolder.primaryActionView.setTag(viewHolder);
return viewHolder;
@@ -481,9 +387,9 @@
protected void bindVoicemailPromoCardViewHolder(ViewHolder viewHolder) {
PromoCardViewHolder promoCardViewHolder = (PromoCardViewHolder) viewHolder;
- promoCardViewHolder.getSettingsTextView().setOnClickListener(
- mVoicemailSettingsActionListener);
- promoCardViewHolder.getOkTextView().setOnClickListener(mOkActionListener);
+ promoCardViewHolder.getSecondaryActionView()
+ .setOnClickListener(mVoicemailSettingsActionListener);
+ promoCardViewHolder.getPrimaryActionView().setOnClickListener(mOkActionListener);
}
/**
diff --git a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
index 1becc89..5a62a7d 100644
--- a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
+++ b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
@@ -29,6 +29,7 @@
import android.util.Log;
import com.android.contacts.common.GeoUtil;
+import com.android.dialer.DialtactsActivity;
import com.android.dialer.PhoneCallDetails;
import com.android.dialer.util.AsyncTaskExecutor;
import com.android.dialer.util.AsyncTaskExecutors;
@@ -246,6 +247,8 @@
Intent intent = new Intent(context, CallLogNotificationsService.class);
intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_VOICEMAILS_AS_OLD);
context.startService(intent);
+
+ ((DialtactsActivity) context).updateTabUnreadCounts();
return null;
}
});
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index 09e4f02..8762f18 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -284,6 +284,9 @@
}
@Override
+ public void onVoicemailUnreadCountFetched(Cursor cursor) {}
+
+ @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
View view = inflater.inflate(R.layout.call_log_fragment, container, false);
diff --git a/src/com/android/dialer/calllog/CallLogListItemHelper.java b/src/com/android/dialer/calllog/CallLogListItemHelper.java
index 1c8e397..d18e274 100644
--- a/src/com/android/dialer/calllog/CallLogListItemHelper.java
+++ b/src/com/android/dialer/calllog/CallLogListItemHelper.java
@@ -74,6 +74,9 @@
// 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 d85deb3..521b2a4 100644
--- a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
+++ b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
@@ -21,12 +21,15 @@
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.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 @@
* 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 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 @@
private final TelecomCallLogCache mTelecomCallLogCache;
private final CallLogListItemHelper mCallLogListItemHelper;
private final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
+ private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
private final int mPhotoSize;
@@ -168,6 +190,7 @@
TelecomCallLogCache telecomCallLogCache,
CallLogListItemHelper callLogListItemHelper,
VoicemailPlaybackPresenter voicemailPlaybackPresenter,
+ FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler,
View rootView,
QuickContactBadge quickContactView,
View primaryActionView,
@@ -182,6 +205,7 @@
mTelecomCallLogCache = telecomCallLogCache;
mCallLogListItemHelper = callLogListItemHelper;
mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
+ mFilteredNumberAsyncQueryHandler = filteredNumberAsyncQueryHandler;
this.rootView = rootView;
this.quickContactView = quickContactView;
@@ -202,6 +226,7 @@
primaryActionButtonView.setOnClickListener(this);
primaryActionView.setOnClickListener(mExpandCollapseListener);
+ primaryActionView.setOnCreateContextMenuListener(this);
}
public static CallLogListItemViewHolder create(
@@ -210,7 +235,8 @@
View.OnClickListener expandCollapseListener,
TelecomCallLogCache telecomCallLogCache,
CallLogListItemHelper callLogListItemHelper,
- VoicemailPlaybackPresenter voicemailPlaybackPresenter) {
+ VoicemailPlaybackPresenter voicemailPlaybackPresenter,
+ FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler) {
return new CallLogListItemViewHolder(
context,
@@ -218,6 +244,7 @@
telecomCallLogCache,
callLogListItemHelper,
voicemailPlaybackPresenter,
+ filteredNumberAsyncQueryHandler,
view,
(QuickContactBadge) view.findViewById(R.id.quick_contact_photo),
view.findViewById(R.id.primary_action_view),
@@ -227,6 +254,92 @@
(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 @@
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/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java
index 60bdcff..771df99 100644
--- a/src/com/android/dialer/calllog/CallLogQueryHandler.java
+++ b/src/com/android/dialer/calllog/CallLogQueryHandler.java
@@ -59,6 +59,8 @@
private static final int UPDATE_MARK_MISSED_CALL_AS_READ_TOKEN = 56;
/** The token for the query to fetch voicemail status messages. */
private static final int QUERY_VOICEMAIL_STATUS_TOKEN = 57;
+ /** The token for the query to fetch the number of unread voicemails. */
+ private static final int QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN = 58;
private final int mLogLimit;
@@ -147,6 +149,13 @@
}
}
+ public void fetchVoicemailUnreadCount() {
+ if (TelecomUtil.hasReadWriteVoicemailPermissions(mContext)) {
+ startQuery(QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN, null, Voicemails.CONTENT_URI,
+ new String[] { Voicemails._ID }, Voicemails.IS_READ + "=0", null, null);
+ }
+ }
+
/** Fetches the list of calls in the call log. */
private void fetchCalls(int token, int callType, boolean newOnly, long newerThan) {
// We need to check for NULL explicitly otherwise entries with where READ is NULL
@@ -243,6 +252,8 @@
}
} else if (token == QUERY_VOICEMAIL_STATUS_TOKEN) {
updateVoicemailStatus(cursor);
+ } else if (token == QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN) {
+ updateVoicemailUnreadCount(cursor);
} else {
Log.w(TAG, "Unknown query completed: ignoring: " + token);
}
@@ -273,11 +284,21 @@
}
}
+ private void updateVoicemailUnreadCount(Cursor statusCursor) {
+ final Listener listener = mListener.get();
+ if (listener != null) {
+ listener.onVoicemailUnreadCountFetched(statusCursor);
+ }
+ }
+
/** Listener to completion of various queries. */
public interface Listener {
/** Called when {@link CallLogQueryHandler#fetchVoicemailStatus()} completes. */
void onVoicemailStatusFetched(Cursor statusCursor);
+ /** Called when {@link CallLogQueryHandler#fetchVoicemailUnreadCount()} completes. */
+ void onVoicemailUnreadCountFetched(Cursor cursor);
+
/**
* Called when {@link CallLogQueryHandler#fetchCalls(int)} complete.
* Returns true if takes ownership of cursor.
diff --git a/src/com/android/dialer/calllog/GroupingListAdapter.java b/src/com/android/dialer/calllog/GroupingListAdapter.java
index 54dd5f6..70190df 100644
--- a/src/com/android/dialer/calllog/GroupingListAdapter.java
+++ b/src/com/android/dialer/calllog/GroupingListAdapter.java
@@ -126,7 +126,7 @@
* that position.
*/
public int getGroupSize(int listPosition) {
- if (listPosition >= mGroupMetadata.size()) {
+ if (listPosition < 0 || listPosition >= mGroupMetadata.size()) {
return 0;
}
@@ -138,7 +138,7 @@
* corresponding to that position.
*/
public Object getItem(int listPosition) {
- if (mCursor == null || listPosition >= mGroupMetadata.size()) {
+ if (mCursor == null || listPosition < 0 || listPosition >= mGroupMetadata.size()) {
return null;
}
diff --git a/src/com/android/dialer/calllog/PromoCardViewHolder.java b/src/com/android/dialer/calllog/PromoCardViewHolder.java
index 656b669..f5a7501 100644
--- a/src/com/android/dialer/calllog/PromoCardViewHolder.java
+++ b/src/com/android/dialer/calllog/PromoCardViewHolder.java
@@ -24,7 +24,8 @@
import com.android.dialer.R;
/**
- * View holder class for a promo card which will appear in the voicemail tab.
+ * Generic ViewHolder class for a promo card with a primary and secondary action.
+ * Example: the promo card which appears in the voicemail tab.
*/
public class PromoCardViewHolder extends RecyclerView.ViewHolder {
public static PromoCardViewHolder create(View rootView) {
@@ -32,14 +33,15 @@
}
/**
- * The "Settings" button view.
+ * The primary action button view.
*/
- private View mSettingsTextView;
+ private View mPrimaryActionView;
/**
+ * The secondary action button view.
* The "Ok" button view.
*/
- private View mOkTextView;
+ private View mSecondaryActionView;
/**
* Creates an instance of the {@link ViewHolder}.
@@ -49,33 +51,33 @@
private PromoCardViewHolder(View rootView) {
super(rootView);
- mSettingsTextView = rootView.findViewById(R.id.settings_action);
- mOkTextView = rootView.findViewById(R.id.ok_action);
+ mPrimaryActionView = rootView.findViewById(R.id.primary_action);
+ mSecondaryActionView = rootView.findViewById(R.id.secondary_action);
}
- /**
- * Retrieves the "Settings" button.
+ /**
+ * Retrieves the "primary" action button (eg. "OK").
*
* @return The view.
*/
- public View getSettingsTextView() {
- return mSettingsTextView;
+ public View getPrimaryActionView() {
+ return mPrimaryActionView;
}
/**
- * Retrieves the "Ok" button.
+ * Retrieves the "secondary" action button (eg. "Cancel" or "More Info").
*
* @return The view.
*/
- public View getOkTextView() {
- return mOkTextView;
+ public View getSecondaryActionView() {
+ return mSecondaryActionView;
}
@NeededForTesting
public static PromoCardViewHolder createForTest(Context context) {
PromoCardViewHolder viewHolder = new PromoCardViewHolder(new View(context));
- viewHolder.mSettingsTextView = new View(context);
- viewHolder.mOkTextView = new View(context);
+ viewHolder.mPrimaryActionView = new View(context);
+ viewHolder.mSecondaryActionView = new View(context);
return viewHolder;
}
}
diff --git a/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java b/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
index 58a717b..2fdea0d 100644
--- a/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
+++ b/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
@@ -21,6 +21,8 @@
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 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
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() {
- @Override
- public void onDeleteComplete(int token, Object cookie, int result) {
- listener.onDeleteComplete(result);
- }
- }, getContentUri(id), null, null);
+ 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 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);
+ }
+ }, 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 01dc892..0bbf802 100644
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ b/src/com/android/dialer/dialpad/DialpadFragment.java
@@ -481,7 +481,10 @@
* @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 0000000..f94d0f8
--- /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
diff --git a/src/com/android/dialer/list/ListsFragment.java b/src/com/android/dialer/list/ListsFragment.java
index feab201..09a4cb2 100644
--- a/src/com/android/dialer/list/ListsFragment.java
+++ b/src/com/android/dialer/list/ListsFragment.java
@@ -110,6 +110,7 @@
* The position of the currently selected tab.
*/
private int mTabIndex = TAB_INDEX_SPEED_DIAL;
+ private CallLogQueryHandler mCallLogQueryHandler;
public class ViewPagerAdapter extends FragmentPagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
@@ -199,9 +200,9 @@
}
// Fetch voicemail status to determine if we should show the voicemail tab.
- CallLogQueryHandler callLogQueryHandler =
+ mCallLogQueryHandler =
new CallLogQueryHandler(getActivity(), getActivity().getContentResolver(), this);
- callLogQueryHandler.fetchVoicemailStatus();
+ mCallLogQueryHandler.fetchVoicemailStatus();
Trace.endSection();
}
@@ -227,13 +228,13 @@
mTabTitles[TAB_INDEX_VOICEMAIL] = getResources().getString(R.string.tab_voicemail);
mTabIcons = new int[TAB_COUNT_WITH_VOICEMAIL];
- mTabIcons[TAB_INDEX_SPEED_DIAL] = R.drawable.tab_speed_dial;
- mTabIcons[TAB_INDEX_HISTORY] = R.drawable.tab_history;
- mTabIcons[TAB_INDEX_ALL_CONTACTS] = R.drawable.tab_contacts;
- mTabIcons[TAB_INDEX_VOICEMAIL] = R.drawable.tab_voicemail;
+ mTabIcons[TAB_INDEX_SPEED_DIAL] = R.drawable.ic_grade_24dp;
+ mTabIcons[TAB_INDEX_HISTORY] = R.drawable.ic_schedule_24dp;
+ mTabIcons[TAB_INDEX_ALL_CONTACTS] = R.drawable.ic_people_24dp;
+ mTabIcons[TAB_INDEX_VOICEMAIL] = R.drawable.ic_voicemail_24dp;
mViewPagerTabs = (ViewPagerTabs) parentView.findViewById(R.id.lists_pager_header);
- mViewPagerTabs.setTabIcons(mTabIcons);
+ mViewPagerTabs.configureTabIcons(mTabIcons);
mViewPagerTabs.setViewPager(mViewPager);
addOnPageChangeListener(mViewPagerTabs);
@@ -319,8 +320,12 @@
mViewPagerTabs.setViewPager(mViewPager);
mPrefs.edit()
- .putBoolean(PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, hasActiveVoicemailProvider)
- .commit();
+ .putBoolean(PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, hasActiveVoicemailProvider)
+ .commit();
+ }
+
+ if (hasActiveVoicemailProvider) {
+ mCallLogQueryHandler.fetchVoicemailUnreadCount();
}
if (mHasActiveVoicemailProvider && mShowVoicemailTabAfterVoicemailStatusIsFetched) {
@@ -330,6 +335,23 @@
}
@Override
+ public void onVoicemailUnreadCountFetched(Cursor cursor) {
+ if (getActivity() == null || getActivity().isFinishing() || cursor == null) {
+ return;
+ }
+
+ int count = 0;
+ try {
+ count = cursor.getCount();
+ } finally {
+ cursor.close();
+ }
+
+ mViewPagerTabs.setUnreadCount(count, TAB_INDEX_VOICEMAIL);
+ mViewPagerTabs.setViewPager(mViewPager);
+ }
+
+ @Override
public boolean onCallsFetched(Cursor statusCursor) {
// Return false; did not take ownership of cursor
return false;
@@ -339,6 +361,16 @@
return mTabIndex;
}
+ /**
+ * External method to update unread count because the unread count changes when the user
+ * expands a voicemail in the call log.
+ */
+ public void updateTabUnreadCounts() {
+ if (mHasActiveVoicemailProvider) {
+ mCallLogQueryHandler.fetchVoicemailUnreadCount();
+ }
+ }
+
public void showRemoveView(boolean show) {
mRemoveViewContent.setVisibility(show ? View.VISIBLE : View.GONE);
mRemoveView.setAlpha(show ? 0 : 1);
diff --git a/tests/src/com/android/dialer/CallDetailActivityTest.java b/tests/src/com/android/dialer/CallDetailActivityTest.java
index c874244..59c2434 100644
--- a/tests/src/com/android/dialer/CallDetailActivityTest.java
+++ b/tests/src/com/android/dialer/CallDetailActivityTest.java
@@ -29,12 +29,12 @@
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.Suppress;
import android.view.Menu;
+import android.widget.PopupMenu;
import android.widget.TextView;
import com.android.dialer.calllog.CallLogAsyncTaskUtil;
import com.android.dialer.util.AsyncTaskExecutors;
import com.android.dialer.util.FakeAsyncTaskExecutor;
-// import com.android.internal.view.menu.ContextMenuBuilder;
/**
* Unit tests for the {@link CallDetailActivity}. NOTE: The screen needs to be on for the
@@ -86,33 +86,33 @@
* Test for bug where voicemails should not have remove-from-call-log entry.
* <p>
* See http://b/5054103.
+ */
public void testVoicemailDoesNotHaveRemoveFromCallLog() throws Throwable {
setActivityIntentForTestVoicemailEntry();
startActivityUnderTest();
mFakeAsyncTaskExecutor.runTask(GET_CALL_DETAILS);
- Menu menu = new ContextMenuBuilder(mActivityUnderTest);
- mActivityUnderTest.onCreateOptionsMenu(menu);
- mActivityUnderTest.onPrepareOptionsMenu(menu);
- assertFalse(menu.findItem(R.id.menu_remove_from_call_log).isVisible());
- assertTrue(menu.findItem(R.id.menu_trash).isVisible());
+ Menu optionsMenu = (new PopupMenu(mActivityUnderTest, null)).getMenu();
+ mActivityUnderTest.onCreateOptionsMenu(optionsMenu);
+ mActivityUnderTest.onPrepareOptionsMenu(optionsMenu);
+ assertFalse(optionsMenu.findItem(R.id.menu_remove_from_call_log).isVisible());
+ assertTrue(optionsMenu.findItem(R.id.menu_trash).isVisible());
}
- */
/**
* Test to check that I haven't broken the remove-from-call-log entry from regular calls.
+ */
public void testRegularCallDoesHaveRemoveFromCallLog() throws Throwable {
setActivityIntentForTestCallEntry();
startActivityUnderTest();
mFakeAsyncTaskExecutor.runTask(GET_CALL_DETAILS);
- Menu menu = new ContextMenuBuilder(mActivityUnderTest);
- mActivityUnderTest.onCreateOptionsMenu(menu);
- mActivityUnderTest.onPrepareOptionsMenu(menu);
- assertTrue(menu.findItem(R.id.menu_remove_from_call_log).isVisible());
- assertFalse(menu.findItem(R.id.menu_trash).isVisible());
+ Menu optionsMenu = (new PopupMenu(mActivityUnderTest, null)).getMenu();
+ mActivityUnderTest.onCreateOptionsMenu(optionsMenu);
+ mActivityUnderTest.onPrepareOptionsMenu(optionsMenu);
+ assertTrue(optionsMenu.findItem(R.id.menu_remove_from_call_log).isVisible());
+ assertFalse(optionsMenu.findItem(R.id.menu_trash).isVisible());
}
- */
private void setActivityIntentForTestCallEntry() {
assertNull(mVoicemailUri);