diff options
author | Tyler Gunn <tgunn@google.com> | 2014-02-03 08:31:10 -0800 |
---|---|---|
committer | Tyler Gunn <tgunn@google.com> | 2014-02-03 08:31:10 -0800 |
commit | 45ed3b5932ed590b45235d7b2befa736a95e7f75 (patch) | |
tree | 472471be0b07bc9becba8270dc4d0e183c4cd49f | |
parent | 964222de32bfb2ca0ff06b445bcb25c0bb936d2f (diff) |
Call History Affordances Swap
1. Swapped the intents for the primary view
2. Changed the icon for the secondary action button
3. Added a vertical divider line to separate the secondary action button from the primary view
4. Cleaned up the accessibility text
Change-Id: Ifd4ceff0d67b1587c4378e29be7344de50057a7d
-rw-r--r-- | res/layout/call_log_list_item.xml | 42 | ||||
-rw-r--r-- | res/values/dimens.xml | 7 | ||||
-rw-r--r-- | res/values/strings.xml | 80 | ||||
-rw-r--r-- | src/com/android/dialer/PhoneCallDetailsHelper.java | 54 | ||||
-rw-r--r-- | src/com/android/dialer/calllog/CallLogAdapter.java | 52 | ||||
-rw-r--r-- | src/com/android/dialer/calllog/CallLogFragment.java | 2 | ||||
-rw-r--r-- | src/com/android/dialer/calllog/CallLogListItemHelper.java | 203 | ||||
-rw-r--r-- | src/com/android/dialer/calllog/CallLogListItemViews.java | 12 | ||||
-rw-r--r-- | src/com/android/dialer/list/PhoneFavoriteFragment.java | 2 | ||||
-rw-r--r-- | src/com/android/dialerbind/ObjectFactory.java | 14 | ||||
-rw-r--r-- | tests/AndroidManifest.xml | 1 | ||||
-rw-r--r-- | tests/src/com/android/dialer/calllog/CallLogFragmentTest.java | 28 | ||||
-rw-r--r-- | tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java | 245 |
13 files changed, 632 insertions, 110 deletions
diff --git a/res/layout/call_log_list_item.xml b/res/layout/call_log_list_item.xml index 1d368f63d..15d7a9275 100644 --- a/res/layout/call_log_list_item.xml +++ b/res/layout/call_log_list_item.xml @@ -114,18 +114,38 @@ /> </LinearLayout> </LinearLayout> - <ImageButton - android:id="@+id/secondary_action_icon" - android:layout_width="@dimen/call_log_call_action_width" + <!-- Linear layout to house a vertical separator line and the + secondary action button. Used as a convenience to hide both + the separator and action button at the same time. --> + <LinearLayout + android:id="@+id/secondary_action_view" + android:layout_width="wrap_content" android:layout_height="match_parent" - android:paddingStart="@dimen/call_log_inner_margin" - android:paddingTop="@dimen/call_log_inner_margin" - android:paddingBottom="@dimen/call_log_inner_margin" - android:paddingEnd="@dimen/call_log_inner_margin" - android:scaleType="center" - android:background="?android:attr/selectableItemBackground" - android:nextFocusLeft="@id/primary_action_view" - /> + android:orientation="horizontal" + android:gravity="center_vertical" + > + <!-- Thin vertical divider to visually separate the secondary action button --> + <View + android:id="@+id/vertical_divider" + android:layout_width="@dimen/call_log_list_item_vertical_divider_width" + android:layout_height="match_parent" + android:layout_marginTop="@dimen/call_log_list_item_vertical_divider_margin" + android:layout_marginBottom="@dimen/call_log_list_item_vertical_divider_margin" + android:background="?android:attr/dividerVertical"/> + <!-- The secondary action button; either play voicemail or call details. --> + <ImageButton + android:id="@+id/secondary_action_icon" + android:layout_width="@dimen/call_log_call_action_width" + android:layout_height="match_parent" + android:paddingStart="@dimen/call_log_inner_margin" + android:paddingTop="@dimen/call_log_inner_margin" + android:paddingBottom="@dimen/call_log_inner_margin" + android:paddingEnd="@dimen/call_log_inner_margin" + android:scaleType="center" + android:background="?android:attr/selectableItemBackground" + android:nextFocusLeft="@id/primary_action_view" + /> + </LinearLayout> </LinearLayout> <TextView diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 17439521b..e84b18a6a 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -43,6 +43,13 @@ <dimen name="call_log_list_contact_photo_size">64dip</dimen> <dimen name="call_detail_contact_name_margin">24dip</dimen> <dimen name="call_detail_button_spacing">2dip</dimen> + <!-- Defines the vertical margin for the vertical separator between + the main area of a call log entry and the secondary action button. --> + <dimen name="call_log_list_item_vertical_divider_margin">8dp</dimen> + + <!-- Defines the width of the vertical separator between + the main area of a call log entry and the secondary action button. --> + <dimen name="call_log_list_item_vertical_divider_width">1dp</dimen> <!-- Layout weight values for dialpad screen. These layouts will be used in one LinearLayout (dialpad_fragment.xml), configuring dialpad screen's vertical diff --git a/res/values/strings.xml b/res/values/strings.xml index b28c8eadf..c8aa37f7e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -441,6 +441,86 @@ --> <string name="description_call">Call <xliff:g id="name">%1$s</xliff:g></string> + <!-- String describing the button to access the contact details for a name or number. + + Note: AccessibilityServices use this attribute to announce what the view represents. + This is especially valuable for views without textual representation like ImageView. + --> + <string name="description_contact_details">Contact details for <xliff:g id="nameOrNumber">%1$s</xliff:g></string> + + <!-- String describing the button to access call details in the call log. + + Note: AccessibilityServices use this attribute to announce what the view represents. + This is especially valuable for views without textual representation like ImageView. + --> + <string name="description_call_details">Call details</string> + + <!-- String indicating a call log entry has an associated voicemail. + + Note: AccessibilityServices use this attribute to announce what the view represents. + This is especially valuable for views without textual representation like ImageView. + --> + <string name="description_new_voicemail">New voicemail.</string> + + <!-- String indicating the number of calls to/from a caller in the call log. + + Note: AccessibilityServices use this attribute to announce what the view represents. + This is especially valuable for views without textual representation like ImageView. + --> + <string name="description_num_calls"><xliff:g id="numberOfCalls">%1$s</xliff:g> calls.</string> + + + <!-- Call history description for a missed call from a caller. + + Note: AccessibilityServices use this attribute to announce what the view represents. + This is especially valuable for views without textual representation like ImageView. + --> + <string name="description_return_missed_call">Return missed call from <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g> <xliff:g id="timeOfCall">%3$s</xliff:g></string> + + + <!-- Call history description for an answered call for a caller. + + Note: AccessibilityServices use this attribute to announce what the view represents. + This is especially valuable for views without textual representation like ImageView. + --> + <string name="description_return_answered_call">Return answered call from <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g> <xliff:g id="timeOfCall">%3$s</xliff:g></string> + + <!-- Call history description for a missed call from an unknown caller. + Drops the "return" part of description_return_missed_call since it is not + possible to actually call an unknown number. + + Note: AccessibilityServices use this attribute to announce what the view represents. + This is especially valuable for views without textual representation like ImageView. + --> + <string name="description_unknown_missed_call">Missed call from <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g> <xliff:g id="timeOfCall">%3$s</xliff:g></string> + + + <!-- Call history description for an answered call from an unknown caller. + Drops the "return" part of description_return_answered_call since it is not + possible to actually call an unknown number. + Note: AccessibilityServices use this attribute to announce what the view represents. + This is especially valuable for views without textual representation like ImageView. + --> + <string name="description_unknown_answered_call">Answered call from <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g> <xliff:g id="timeOfCall">%3$s</xliff:g></string> + + <!-- String describing an outgoing call entry in the call log. Used to indicate that + a call will be made to the specified caller. Used when there are multiple calls to/from + the caller. + + Note: AccessibilityServices use this attribute to announce what the view represents. + This is especially valuable for views without textual representation like ImageView. + --> + <string name="description_call_last_multiple">Call <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g>. Last called <xliff:g id="timeOfCall">%3$s</xliff:g>.</string> + + <!-- String describing an outgoing call entry in the call log. Used to indicate that + a call will be made to the specified caller. Used when there is only a single call + related to/from the caller. + + Note: AccessibilityServices use this attribute to announce what the view represents. + This is especially valuable for views without textual representation like ImageView. + --> + <string name="description_call_last">Call <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g>. Called <xliff:g id="timeOfCall">%3$s</xliff:g>.</string> + <!-- String describing the button to SMS a number or contact. diff --git a/src/com/android/dialer/PhoneCallDetailsHelper.java b/src/com/android/dialer/PhoneCallDetailsHelper.java index 2a4a1425c..4424fcb0b 100644 --- a/src/com/android/dialer/PhoneCallDetailsHelper.java +++ b/src/com/android/dialer/PhoneCallDetailsHelper.java @@ -91,26 +91,13 @@ public class PhoneCallDetailsHelper { isHighlighted ? mCallTypeHelper.getHighlightedColor(details.callTypes[0]) : null; // The date of this call, relative to the current time. - CharSequence dateText = - DateUtils.getRelativeTimeSpanString(details.date, - getCurrentTimeMillis(), - DateUtils.MINUTE_IN_MILLIS, - DateUtils.FORMAT_ABBREV_RELATIVE); + CharSequence dateText = getCallDate(details); // Set the call count and date. setCallCountAndDate(views, callCount, dateText, highlightColor); - CharSequence numberFormattedLabel = null; - // Only show a label if the number is shown and it is not a SIP address. - if (!TextUtils.isEmpty(details.number) - && !PhoneNumberHelper.isUriNumber(details.number.toString())) { - if (details.numberLabel == ContactInfo.GEOCODE_AS_LABEL) { - numberFormattedLabel = details.geocode; - } else { - numberFormattedLabel = Phone.getTypeLabel(mResources, details.numberType, - details.numberLabel); - } - } + // Get type of call (ie mobile, home, etc) if known, or the caller's + CharSequence numberFormattedLabel = getCallTypeOrLocation(details); final CharSequence nameText; final CharSequence numberText; @@ -141,6 +128,41 @@ public class PhoneCallDetailsHelper { views.labelView.setVisibility(TextUtils.isEmpty(labelText) ? View.GONE : View.VISIBLE); } + /** + * For a call, if there is an associated contact for the caller, return the known call type + * (e.g. mobile, home, work). If there is no associated contact, attempt to use the caller's + * location if known. + * @param details Call details to use. + * @return Type of call (mobile/home) if known, or the location of the caller (if known). + */ + public CharSequence getCallTypeOrLocation(PhoneCallDetails details) { + CharSequence numberFormattedLabel = null; + // Only show a label if the number is shown and it is not a SIP address. + if (!TextUtils.isEmpty(details.number) + && !PhoneNumberHelper.isUriNumber(details.number.toString())) { + if (details.numberLabel == ContactInfo.GEOCODE_AS_LABEL) { + numberFormattedLabel = details.geocode; + } else { + numberFormattedLabel = Phone.getTypeLabel(mResources, details.numberType, + details.numberLabel); + } + } + return numberFormattedLabel; + } + + /** + * Get the call date/time of the call, relative to the current time. + * e.g. 3 minutes ago + * @param details Call details to use. + * @return String representing when the call occurred. + */ + public CharSequence getCallDate(PhoneCallDetails details) { + return DateUtils.getRelativeTimeSpanString(details.date, + getCurrentTimeMillis(), + DateUtils.MINUTE_IN_MILLIS, + DateUtils.FORMAT_ABBREV_RELATIVE); + } + /** Sets the text of the header view for the details page of a phone call. */ public void setCallDetailsHeader(TextView nameView, PhoneCallDetails details) { final CharSequence nameText; diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java index a668196c5..32699e654 100644 --- a/src/com/android/dialer/calllog/CallLogAdapter.java +++ b/src/com/android/dialer/calllog/CallLogAdapter.java @@ -183,9 +183,11 @@ public class CallLogAdapter extends GroupingListAdapter /** Can be set to true by tests to disable processing of requests. */ private volatile boolean mRequestProcessingDisabled = false; - /** True if CallLogAdapter is created from the PhoneFavoriteFragment, where the primary - * action should be set to call a number instead of opening the detail page. */ - private boolean mUseCallAsPrimaryAction = false; + /** + * Whether to show the secondary action button used to play voicemail or show call details. + * True if created from a CallLogFragment. + * False if created from the PhoneFavoriteFragment. */ + private boolean mShowSecondaryActionButton = true; private boolean mIsCallLog = true; private int mNumMissedCalls = 0; @@ -246,14 +248,14 @@ public class CallLogAdapter extends GroupingListAdapter }; public CallLogAdapter(Context context, CallFetcher callFetcher, - ContactInfoHelper contactInfoHelper, boolean useCallAsPrimaryAction, + ContactInfoHelper contactInfoHelper, boolean showSecondaryActionButton, boolean isCallLog) { super(context); mContext = context; mCallFetcher = callFetcher; mContactInfoHelper = contactInfoHelper; - mUseCallAsPrimaryAction = useCallAsPrimaryAction; + mShowSecondaryActionButton = showSecondaryActionButton; mIsCallLog = isCallLog; mContactInfoCache = ExpirableCache.create(CONTACT_INFO_CACHE_SIZE); @@ -508,7 +510,7 @@ public class CallLogAdapter extends GroupingListAdapter // Get the views to bind to. CallLogListItemViews views = CallLogListItemViews.fromView(view); views.primaryActionView.setOnClickListener(mActionListener); - views.secondaryActionView.setOnClickListener(mActionListener); + views.secondaryActionButtonView.setOnClickListener(mActionListener); view.setTag(views); } @@ -535,31 +537,30 @@ public class CallLogAdapter extends GroupingListAdapter final ContactInfo cachedContactInfo = getContactInfoFromCallLog(c); - if (!mUseCallAsPrimaryAction) { - // Sets the primary action to open call detail page. - views.primaryActionView.setTag( - IntentProvider.getCallDetailIntentProvider( - getCursor(), c.getPosition(), c.getLong(CallLogQuery.ID), count)); - } else if (PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation)) { + // Primary action is always to call, if possible. + if (PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation)) { // Sets the primary action to call the number. views.primaryActionView.setTag(IntentProvider.getReturnCallIntentProvider(number)); } else { views.primaryActionView.setTag(null); } - // Store away the voicemail information so we can play it directly. - if (callType == Calls.VOICEMAIL_TYPE) { - String voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI); - final long rowId = c.getLong(CallLogQuery.ID); - views.secondaryActionView.setTag( - IntentProvider.getPlayVoicemailIntentProvider(rowId, voicemailUri)); - } else if (!TextUtils.isEmpty(number)) { - // Store away the number so we can call it directly if you click on the call icon. - views.secondaryActionView.setTag( - IntentProvider.getReturnCallIntentProvider(number)); + if ( mShowSecondaryActionButton ) { + // Store away the voicemail information so we can play it directly. + if (callType == Calls.VOICEMAIL_TYPE) { + String voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI); + final long rowId = c.getLong(CallLogQuery.ID); + views.secondaryActionButtonView.setTag( + IntentProvider.getPlayVoicemailIntentProvider(rowId, voicemailUri)); + } else { + // Store the call details information. + views.secondaryActionButtonView.setTag( + IntentProvider.getCallDetailIntentProvider( + getCursor(), c.getPosition(), c.getLong(CallLogQuery.ID), count)); + } } else { // No action enabled. - views.secondaryActionView.setTag(null); + views.secondaryActionButtonView.setTag(null); } // Lookup contacts with this number @@ -624,7 +625,7 @@ public class CallLogAdapter extends GroupingListAdapter // New items also use the highlighted version of the text. final boolean isHighlighted = isNew; mCallLogViewsHelper.setPhoneCallDetails(views, details, isHighlighted, - mUseCallAsPrimaryAction); + mShowSecondaryActionButton); if (photoId == 0 && photoUri != null) { setPhoto(views, photoUri, lookupUri); @@ -632,9 +633,6 @@ public class CallLogAdapter extends GroupingListAdapter setPhoto(views, photoId, lookupUri); } - views.quickContactView.setContentDescription(views.phoneCallDetailsViews.nameView. - getText()); - // Listen for the first draw if (mViewTreeObserver == null) { mViewTreeObserver = view.getViewTreeObserver(); diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java index 826abe11d..9526f392b 100644 --- a/src/com/android/dialer/calllog/CallLogFragment.java +++ b/src/com/android/dialer/calllog/CallLogFragment.java @@ -235,7 +235,7 @@ public class CallLogFragment extends ListFragment updateEmptyMessage(mCallTypeFilter); String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity()); mAdapter = ObjectFactory.newCallLogAdapter(getActivity(), this, new ContactInfoHelper( - getActivity(), currentCountryIso), false, true); + getActivity(), currentCountryIso), true, true); setListAdapter(mAdapter); getListView().setItemsCanFocus(true); } diff --git a/src/com/android/dialer/calllog/CallLogListItemHelper.java b/src/com/android/dialer/calllog/CallLogListItemHelper.java index a38ef012b..a85cd019e 100644 --- a/src/com/android/dialer/calllog/CallLogListItemHelper.java +++ b/src/com/android/dialer/calllog/CallLogListItemHelper.java @@ -17,6 +17,7 @@ package com.android.dialer.calllog; import android.content.res.Resources; +import android.provider.CallLog; import android.provider.CallLog.Calls; import android.text.TextUtils; import android.view.View; @@ -55,37 +56,201 @@ import com.android.dialer.R; * @param views the views to populate * @param details the details of a phone call needed to fill in the data * @param isHighlighted whether to use the highlight text for the call + * @param showSecondaryActionButton whether to show the secondary action button or not */ public void setPhoneCallDetails(CallLogListItemViews views, PhoneCallDetails details, - boolean isHighlighted, boolean useCallAsPrimaryAction) { + boolean isHighlighted, boolean showSecondaryActionButton) { mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details, isHighlighted); - boolean canCall = PhoneNumberUtilsWrapper.canPlaceCallsTo(details.number, - details.numberPresentation); boolean canPlay = details.callTypes[0] == Calls.VOICEMAIL_TYPE; - if (canPlay) { - // Playback action takes preference. - configurePlaySecondaryAction(views, isHighlighted); - } else if (canCall && !useCallAsPrimaryAction) { - // Call is the secondary action. - configureCallSecondaryAction(views, details); + // Set the accessibility text for the contact badge + views.quickContactView.setContentDescription(getContactBadgeDescription(details)); + + // Set the primary action accessibility description + views.primaryActionView.setContentDescription(getCallDescription(details)); + + // If secondary action is visible, either show voicemail playback icon, or + // show the "clock" icon corresponding to the call details screen. + if (showSecondaryActionButton) { + if (canPlay) { + // Playback action takes preference. + configurePlaySecondaryAction(views, isHighlighted); + } else { + // Call details is the secondary action. + configureCallDetailsSecondaryAction(views, details); + } } else { - // No action available. + // No secondary action is to be shown (ie this is likely a PhoneFavoriteFragment) views.secondaryActionView.setVisibility(View.GONE); } } - /** Sets the secondary action to correspond to the call button. */ - private void configureCallSecondaryAction(CallLogListItemViews views, + /** + * Sets the secondary action to invoke call details. + * + * @param views the views to populate + * @param details the details of a phone call needed to fill in the call details data + */ + private void configureCallDetailsSecondaryAction(CallLogListItemViews views, PhoneCallDetails details) { views.secondaryActionView.setVisibility(View.VISIBLE); - views.secondaryActionView.setImageResource(R.drawable.ic_phone_dk); - views.secondaryActionView.setContentDescription(getCallActionDescription(details)); + // Use the small dark grey clock icon. + views.secondaryActionButtonView.setImageResource(R.drawable.ic_menu_history_dk); + views.secondaryActionButtonView.setContentDescription( + mResources.getString(R.string.description_call_details)); + } + + /** + * Returns the accessibility description for the contact badge for a call log entry. + * + * @param details Details of call. + * @return Accessibility description. + */ + private CharSequence getContactBadgeDescription(PhoneCallDetails details) { + return mResources.getString(R.string.description_contact_details, getNameOrNumber(details)); } - /** Returns the description used by the call action for this phone call. */ - private CharSequence getCallActionDescription(PhoneCallDetails details) { + /** + * Returns the accessibility description of the "return call/call" action for a call log + * entry. + * Accessibility text is a combination of: + * {Voicemail Prefix}. {Number of Calls}. {Caller information}. + * If most recent call is a voicemail, {Voicemail Prefix} is "New Voicemail.", otherwise "". + * + * If more than one call for the caller, {Number of Calls} is: + * "{number of calls} calls.", otherwise "". + * + * The {Caller Information} references the most recent call associated with the caller. + * For incoming calls: + * If missed call: Return missed call from {Name/Number} {Call Type} {Call Time}. + * If answered call: Return answered call from {Name/Number} {Call Type} {Call Time}. + * + * For unknown callers, drop the "Return" part, since the call can't be returned: + * If answered unknown: Answered call from {Name/Number} {Call Time}. + * If missed unknown: Missed call from {Name/Number} {Call Time}. + * + * For outgoing calls: + * If outgoing: Call {Name/Number] {Call Type}. {Last} called {Call Time}. + * Where {Last} is dropped if the number of calls for the caller is 1. + * + * Where: + * {Name/Number} is the name or number of the caller (as shown in call log). + * {Call type} is the contact phone number type (eg mobile) or location. + * {Call Time} is the time since the last call for the contact occurred. + * + * Examples: + * 3 calls. New Voicemail. Return missed call from Joe Smith mobile 2 hours ago. + * 2 calls. Call John Doe mobile. Last called 1 hour ago. + * @param details Details of call. + * @return Return call action description. + */ + public CharSequence getCallDescription(PhoneCallDetails details) { + int lastCallType = getLastCallType(details.callTypes); + boolean isVoiceMail = lastCallType == Calls.VOICEMAIL_TYPE; + + // Get the name or number of the caller. + final CharSequence nameOrNumber = getNameOrNumber(details); + + // Get the call type or location of the caller; null if not applicable + final CharSequence typeOrLocation = mPhoneCallDetailsHelper.getCallTypeOrLocation(details); + + // Get the time/date of the call + final CharSequence timeOfCall = mPhoneCallDetailsHelper.getCallDate(details); + + StringBuilder callDescription = new StringBuilder(); + + // Prepend the voicemail indication. + if (isVoiceMail) { + callDescription.append(mResources.getString(R.string.description_new_voicemail)); + } + + // Add number of calls if more than one. + if (details.callTypes.length > 1) { + callDescription.append(mResources.getString(R.string.description_num_calls, + details.callTypes.length)); + } + + int stringID = getCallDescriptionStringID(details); + + // Use chosen string resource to build up the message. + callDescription.append(mResources.getString(stringID, + nameOrNumber, + // If no type or location can be determined, sub in empty string. + typeOrLocation == null ? "" : typeOrLocation, + timeOfCall)); + + return callDescription; + } + + /** + * Determine the appropriate string ID to describe a call for accessibility purposes. + * + * @param details Call details. + * @return String resource ID to use. + */ + public int getCallDescriptionStringID(PhoneCallDetails details) { + int lastCallType = getLastCallType(details.callTypes); + boolean isNumberCallable = PhoneNumberUtilsWrapper.canPlaceCallsTo(details.number, + details.numberPresentation); + + // Default string to use is "call XYZ..." just in case we manage to fall through. + int stringID = R.string.description_call_last_multiple; + + if (!isNumberCallable) { + // Number isn't callable; this is an incoming call from an unknown caller. + // An uncallable outgoing call wouldn't be in the call log. + + // Voicemail and missed calls are both considered missed. + if (lastCallType == Calls.VOICEMAIL_TYPE || + lastCallType == Calls.MISSED_TYPE) { + stringID = R.string.description_unknown_missed_call; + } else if (lastCallType == Calls.INCOMING_TYPE) { + stringID = R.string.description_unknown_answered_call; + } + } else { + // Known caller, so callable. + + // Missed call (ie voicemail or missed) + if (lastCallType == Calls.VOICEMAIL_TYPE || + lastCallType == Calls.MISSED_TYPE) { + stringID = R.string.description_return_missed_call; + } else if (lastCallType == Calls.INCOMING_TYPE) { + // Incoming answered. + stringID = R.string.description_return_answered_call; + } else { + // Outgoing call. + + // If we have a history of multiple calls + if (details.callTypes.length > 1) { + stringID = R.string.description_call_last_multiple; + } else { + stringID = R.string.description_call_last; + } + } + } + return stringID; + } + + /** + * Determine the call type for the most recent call. + * @param callTypes Call types to check. + * @return Call type. + */ + private int getLastCallType(int[] callTypes) { + if (callTypes.length > 0) { + return callTypes[0]; + } else { + return Calls.MISSED_TYPE; + } + } + + /** + * Return the name or number of the caller specified by the details. + * @param details Call details + * @return the name (if known) of the caller, otherwise the formatted number. + */ + private CharSequence getNameOrNumber(PhoneCallDetails details) { final CharSequence recipient; if (!TextUtils.isEmpty(details.name)) { recipient = details.name; @@ -93,15 +258,15 @@ import com.android.dialer.R; recipient = mPhoneNumberHelper.getDisplayNumber( details.number, details.numberPresentation, details.formattedNumber); } - return mResources.getString(R.string.description_call, recipient); + return recipient; } /** Sets the secondary action to correspond to the play button. */ private void configurePlaySecondaryAction(CallLogListItemViews views, boolean isHighlighted) { views.secondaryActionView.setVisibility(View.VISIBLE); - views.secondaryActionView.setImageResource( + views.secondaryActionButtonView.setImageResource( isHighlighted ? R.drawable.ic_play_active_holo_dark : R.drawable.ic_play_holo_light); - views.secondaryActionView.setContentDescription( + views.secondaryActionButtonView.setContentDescription( mResources.getString(R.string.description_call_log_play_button)); } } diff --git a/src/com/android/dialer/calllog/CallLogListItemViews.java b/src/com/android/dialer/calllog/CallLogListItemViews.java index ed6218f86..a3789569e 100644 --- a/src/com/android/dialer/calllog/CallLogListItemViews.java +++ b/src/com/android/dialer/calllog/CallLogListItemViews.java @@ -34,19 +34,25 @@ public final class CallLogListItemViews { public final QuickContactBadge quickContactView; /** The primary action view of the entry. */ public final View primaryActionView; + /** The secondary action view, which includes both the vertical divider line and + * the action button itself. Used so that the button and divider line can be + * made visible/hidden as a whole. */ + public final View secondaryActionView; /** The secondary action button on the entry. */ - public final ImageView secondaryActionView; + public final ImageView secondaryActionButtonView; /** The details of the phone call. */ public final PhoneCallDetailsViews phoneCallDetailsViews; /** The text of the header of a section. */ public final TextView listHeaderTextView; private CallLogListItemViews(QuickContactBadge quickContactView, View primaryActionView, - ImageView secondaryActionView, PhoneCallDetailsViews phoneCallDetailsViews, + View secondaryActionView, ImageView secondaryActionButtonView, + PhoneCallDetailsViews phoneCallDetailsViews, TextView listHeaderTextView) { this.quickContactView = quickContactView; this.primaryActionView = primaryActionView; this.secondaryActionView = secondaryActionView; + this.secondaryActionButtonView = secondaryActionButtonView; this.phoneCallDetailsViews = phoneCallDetailsViews; this.listHeaderTextView = listHeaderTextView; } @@ -55,6 +61,7 @@ public final class CallLogListItemViews { return new CallLogListItemViews( (QuickContactBadge) view.findViewById(R.id.quick_contact_photo), view.findViewById(R.id.primary_action_view), + view.findViewById(R.id.secondary_action_view), (ImageView) view.findViewById(R.id.secondary_action_icon), PhoneCallDetailsViews.fromView(view), (TextView) view.findViewById(R.id.call_log_header)); @@ -65,6 +72,7 @@ public final class CallLogListItemViews { return new CallLogListItemViews( new QuickContactBadge(context), new View(context), + new View(context), new ImageView(context), PhoneCallDetailsViews.createForTest(context), new TextView(context)); diff --git a/src/com/android/dialer/list/PhoneFavoriteFragment.java b/src/com/android/dialer/list/PhoneFavoriteFragment.java index 81d9bfdc8..2791f1569 100644 --- a/src/com/android/dialer/list/PhoneFavoriteFragment.java +++ b/src/com/android/dialer/list/PhoneFavoriteFragment.java @@ -253,7 +253,7 @@ public class PhoneFavoriteFragment extends Fragment implements OnItemClickListen this, 1); final String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity()); mCallLogAdapter = ObjectFactory.newCallLogAdapter(getActivity(), this, - new ContactInfoHelper(getActivity(), currentCountryIso), true, false); + new ContactInfoHelper(getActivity(), currentCountryIso), false, false); setHasOptionsMenu(true); } diff --git a/src/com/android/dialerbind/ObjectFactory.java b/src/com/android/dialerbind/ObjectFactory.java index c43dffcea..be91e3310 100644 --- a/src/com/android/dialerbind/ObjectFactory.java +++ b/src/com/android/dialerbind/ObjectFactory.java @@ -34,10 +34,20 @@ public class ObjectFactory { return null; } + /** + * Create a new instance of the call log adapter. + * @param context The context to use. + * @param callFetcher Instance of call fetcher to use. + * @param contactInfoHelper Instance of contact info helper class to use. + * @param hideSecondaryAction If true, secondary action will be hidden (ie call details + * or play voicemail). + * @param isCallLog Is this call log adapter being used on the call log? + * @return Instance of CallLogAdapter. + */ public static CallLogAdapter newCallLogAdapter(Context context, CallFetcher callFetcher, - ContactInfoHelper contactInfoHelper, boolean useCallAsPrimaryAction, + ContactInfoHelper contactInfoHelper, boolean hideSecondaryAction, boolean isCallLog) { - return new CallLogAdapter(context, callFetcher, contactInfoHelper, useCallAsPrimaryAction, + return new CallLogAdapter(context, callFetcher, contactInfoHelper, hideSecondaryAction, isCallLog); } } diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml index 3a714e39a..94aa3aada 100644 --- a/tests/AndroidManifest.xml +++ b/tests/AndroidManifest.xml @@ -25,6 +25,7 @@ <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.USE_CREDENTIALS" /> + <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" /> <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" /> <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" /> <uses-permission android:name="android.permission.READ_SYNC_STATS" /> diff --git a/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java b/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java index d0a87259c..25f53790b 100644 --- a/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java +++ b/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java @@ -118,7 +118,7 @@ public class CallLogFragmentTest extends ActivityInstrumentationTestCase2<Fragme FragmentManager fragmentManager = mActivity.getFragmentManager(); FragmentTransaction transaction = fragmentManager.beginTransaction(); transaction.add(FragmentTestActivity.LAYOUT_ID, mFragment); - transaction.commit(); + transaction.commitAllowingStateLoss(); // Wait for the fragment to be loaded. getInstrumentation().waitForIdleSync(); @@ -323,7 +323,12 @@ public class CallLogFragmentTest extends ActivityInstrumentationTestCase2<Fragme mAdapter.bindStandAloneView(view, getActivity(), mCursor); CallLogListItemViews views = (CallLogListItemViews) view.getTag(); - IntentProvider intentProvider = (IntentProvider) views.secondaryActionView.getTag(); + + // The primaryActionView tag is set in the + // {@link com.android.dialer.calllog.CallLogAdapter#bindView} method. If it is possible + // to place a call to the phone number, a call intent will have been created for the + // primaryActionView. + IntentProvider intentProvider = (IntentProvider) views.primaryActionView.getTag(); Intent intent = intentProvider.getIntent(mActivity); // Starts a call. assertEquals(Intent.ACTION_CALL_PRIVILEGED, intent.getAction()); @@ -339,7 +344,7 @@ public class CallLogFragmentTest extends ActivityInstrumentationTestCase2<Fragme mAdapter.bindStandAloneView(view, getActivity(), mCursor); CallLogListItemViews views = (CallLogListItemViews) view.getTag(); - IntentProvider intentProvider = (IntentProvider) views.secondaryActionView.getTag(); + IntentProvider intentProvider = (IntentProvider) views.secondaryActionButtonView.getTag(); Intent intent = intentProvider.getIntent(mActivity); // Starts the call detail activity. assertEquals(new ComponentName(mActivity, CallDetailActivity.class), @@ -365,8 +370,9 @@ public class CallLogFragmentTest extends ActivityInstrumentationTestCase2<Fragme // HELPERS to check conditions on the DB/views // /** - * Go over all the views in the list and check that the Call - * icon's visibility matches the nature of the number. + * Go over the views in the list and check to ensure that + * callable numbers have an associated call intent, where numbers + * which are not callable have a null intent. */ private void checkCallStatus() { for (int i = 0; i < mList.length; i++) { @@ -377,9 +383,17 @@ public class CallLogFragmentTest extends ActivityInstrumentationTestCase2<Fragme int presentation = getPhoneNumberPresentationForListEntry(i); if (presentation == Calls.PRESENTATION_RESTRICTED || presentation == Calls.PRESENTATION_UNKNOWN) { - assertFalse(View.VISIBLE == mItem.secondaryActionView.getVisibility()); + //If number is not callable, the primary action view should have a null tag. + assertNull(mItem.primaryActionView.getTag()); } else { - assertEquals(View.VISIBLE, mItem.secondaryActionView.getVisibility()); + //If the number is callable, the primary action view should have a non-null tag. + assertNotNull(mItem.primaryActionView.getTag()); + + IntentProvider intentProvider = (IntentProvider)mItem.primaryActionView.getTag(); + Intent callIntent = intentProvider.getIntent(mActivity); + + //The intent should be to make the call + assertEquals(Intent.ACTION_CALL_PRIVILEGED, callIntent.getAction()); } } } diff --git a/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java b/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java index a10dec908..7e4736e35 100644 --- a/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java +++ b/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java @@ -24,6 +24,7 @@ import android.view.View; import com.android.dialer.PhoneCallDetails; import com.android.dialer.PhoneCallDetailsHelper; +import com.android.dialer.R; /** * Unit tests for {@link CallLogListItemHelper}. @@ -52,19 +53,21 @@ public class CallLogListItemHelperTest extends AndroidTestCase { private PhoneNumberDisplayHelper mPhoneNumberHelper; private PhoneNumberDisplayHelper mPhoneNumberDisplayHelper; + private Resources mResources; + @Override protected void setUp() throws Exception { super.setUp(); Context context = getContext(); - Resources resources = context.getResources(); - CallTypeHelper callTypeHelper = new CallTypeHelper(resources); + mResources = context.getResources(); + CallTypeHelper callTypeHelper = new CallTypeHelper(mResources); final TestPhoneNumberUtilsWrapper phoneUtils = new TestPhoneNumberUtilsWrapper( TEST_VOICEMAIL_NUMBER); PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper( - resources, callTypeHelper, phoneUtils); - mPhoneNumberDisplayHelper = new PhoneNumberDisplayHelper(resources); + mResources, callTypeHelper, phoneUtils); + mPhoneNumberDisplayHelper = new PhoneNumberDisplayHelper(mResources); mHelper = new CallLogListItemHelper(phoneCallDetailsHelper, mPhoneNumberDisplayHelper, - resources); + mResources); mViews = CallLogListItemViews.createForTest(context); } @@ -78,92 +81,286 @@ public class CallLogListItemHelperTest extends AndroidTestCase { public void testSetPhoneCallDetails() { setPhoneCallDetailsWithNumber("12125551234", Calls.PRESENTATION_ALLOWED, "1-212-555-1234"); - assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility()); + assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility()); } public void testSetPhoneCallDetailsInFavorites() { setPhoneCallDetailsWithNumberInFavorites("12125551234", Calls.PRESENTATION_ALLOWED, "1-212-555-1234"); - assertNoCallButton(); + assertNoCallIntent(); } public void testSetPhoneCallDetails_Unknown() { setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_UNKNOWN, ""); - assertNoCallButton(); + assertNoCallIntent(); } public void testSetPhoneCallDetailsInFavorites_Unknown() { setPhoneCallDetailsWithNumberInFavorites("", Calls.PRESENTATION_UNKNOWN, ""); - assertNoCallButton(); + assertNoCallIntent(); } public void testSetPhoneCallDetails_Private() { setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_RESTRICTED, ""); - assertNoCallButton(); + assertNoCallIntent(); } public void testSetPhoneCallDetailsInFavorites_Private() { setPhoneCallDetailsWithNumberInFavorites("", Calls.PRESENTATION_RESTRICTED, ""); - assertNoCallButton(); + assertNoCallIntent(); } public void testSetPhoneCallDetails_Payphone() { setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_PAYPHONE, ""); - assertNoCallButton(); + assertNoCallIntent(); } public void testSetPhoneCallDetailsInFavorites_Payphone() { setPhoneCallDetailsWithNumberInFavorites("", Calls.PRESENTATION_PAYPHONE, ""); - assertNoCallButton(); + assertNoCallIntent(); } public void testSetPhoneCallDetails_VoicemailNumber() { setPhoneCallDetailsWithNumber(TEST_VOICEMAIL_NUMBER, Calls.PRESENTATION_ALLOWED, TEST_VOICEMAIL_NUMBER); - assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility()); + assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility()); } public void testSetPhoneCallDetails_ReadVoicemail() { setPhoneCallDetailsWithTypes(Calls.VOICEMAIL_TYPE); - assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility()); + assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility()); } public void testSetPhoneCallDetails_UnreadVoicemail() { setUnreadPhoneCallDetailsWithTypes(Calls.VOICEMAIL_TYPE); - assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility()); + assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility()); } public void testSetPhoneCallDetails_VoicemailFromUnknown() { setPhoneCallDetailsWithNumberAndType("", Calls.PRESENTATION_UNKNOWN, "", Calls.VOICEMAIL_TYPE); - assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility()); + assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility()); } public void testSetPhoneCallDetailsInFavorites_VoicemailNumber() { setPhoneCallDetailsWithNumberInFavorites(TEST_VOICEMAIL_NUMBER, Calls.PRESENTATION_ALLOWED, TEST_VOICEMAIL_NUMBER); - assertNoCallButton(); + assertNoCallIntent(); } public void testSetPhoneCallDetailsInFavorites_ReadVoicemail() { setPhoneCallDetailsWithTypesInFavorites(Calls.VOICEMAIL_TYPE); - assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility()); + assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility()); } public void testSetPhoneCallDetailsInFavorites_UnreadVoicemail() { setUnreadPhoneCallDetailsWithTypesInFavorites(Calls.VOICEMAIL_TYPE); - assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility()); + assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility()); } public void testSetPhoneCallDetailsInFavorites_VoicemailFromUnknown() { setPhoneCallDetailsWithNumberAndTypeInFavorites("", Calls.PRESENTATION_UNKNOWN, "", Calls.VOICEMAIL_TYPE); - assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility()); + assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility()); + } + + /** + * Test getCallDescriptionID method used to get the accessibility description for calls. + * Test case where an answered unknown call is received. + */ + public void testGetCallDescriptionID_UnknownAnswered() { + PhoneCallDetails details = new PhoneCallDetails("", Calls.PRESENTATION_UNKNOWN, "", + TEST_COUNTRY_ISO, TEST_GEOCODE, + new int[]{Calls.INCOMING_TYPE}, TEST_DATE, TEST_DURATION); + assertEquals(R.string.description_unknown_answered_call, + mHelper.getCallDescriptionStringID(details)); + } + + /** + * Test getCallDescriptionID method used to get the accessibility description for calls. + * Test case where an missed unknown call is received. + */ + public void testGetCallDescriptionID_UnknownMissed() { + PhoneCallDetails details = new PhoneCallDetails("", Calls.PRESENTATION_UNKNOWN, "", + TEST_COUNTRY_ISO, TEST_GEOCODE, + new int[]{Calls.MISSED_TYPE}, TEST_DATE, TEST_DURATION); + assertEquals(R.string.description_unknown_missed_call, + mHelper.getCallDescriptionStringID(details)); + } + + /** + * Test getCallDescriptionID method used to get the accessibility description for calls. + * Test case where an missed unknown call is received and a voicemail was left. + */ + public void testGetCallDescriptionID_UnknownVoicemail() { + PhoneCallDetails details = new PhoneCallDetails("", Calls.PRESENTATION_UNKNOWN, "", + TEST_COUNTRY_ISO, TEST_GEOCODE, + new int[]{Calls.VOICEMAIL_TYPE}, TEST_DATE, TEST_DURATION); + assertEquals(R.string.description_unknown_missed_call, + mHelper.getCallDescriptionStringID(details)); + } + + /** + * Test getCallDescriptionID method used to get the accessibility description for calls. + * Test case where an answered call from a known caller is received. + */ + public void testGetCallDescriptionID_KnownAnswered() { + PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED, + TEST_FORMATTED_NUMBER, + TEST_COUNTRY_ISO, TEST_GEOCODE, + new int[]{Calls.INCOMING_TYPE}, TEST_DATE, TEST_DURATION); + assertEquals(R.string.description_return_answered_call, + mHelper.getCallDescriptionStringID(details)); + } + + /** + * Test getCallDescriptionID method used to get the accessibility description for calls. + * Test case where a missed call from a known caller is received. + */ + public void testGetCallDescriptionID_KnownMissed() { + PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED, + TEST_FORMATTED_NUMBER, + TEST_COUNTRY_ISO, TEST_GEOCODE, + new int[]{Calls.MISSED_TYPE}, TEST_DATE, TEST_DURATION); + assertEquals(R.string.description_return_missed_call, + mHelper.getCallDescriptionStringID(details)); + } + + /** + * Test getCallDescriptionID method used to get the accessibility description for calls. + * Test case where a missed call from a known caller is received and a voicemail was left. + */ + public void testGetCallDescriptionID_KnownVoicemail() { + PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED, + TEST_FORMATTED_NUMBER, + TEST_COUNTRY_ISO, TEST_GEOCODE, + new int[]{Calls.VOICEMAIL_TYPE}, TEST_DATE, TEST_DURATION); + assertEquals(R.string.description_return_missed_call, + mHelper.getCallDescriptionStringID(details)); + } + + /** + * Test getCallDescriptionID method used to get the accessibility description for calls. + * Test case where an outgoing call is made to a known number and there is a history of + * only a single call for this caller. + */ + public void testGetCallDescriptionID_OutgoingSingle() { + PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED, + TEST_FORMATTED_NUMBER, + TEST_COUNTRY_ISO, TEST_GEOCODE, + new int[]{Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION); + assertEquals(R.string.description_call_last, + mHelper.getCallDescriptionStringID(details)); + } + + /** + * Test getCallDescriptionID method used to get the accessibility description for calls. + * Test case where an outgoing call is made to a known number and there is a history of + * many calls for this caller. + */ + public void testGetCallDescriptionID_OutgoingMultiple() { + PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED, + TEST_FORMATTED_NUMBER, + TEST_COUNTRY_ISO, TEST_GEOCODE, + new int[]{Calls.OUTGOING_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION); + assertEquals(R.string.description_call_last_multiple, + mHelper.getCallDescriptionStringID(details)); + } + + /** + * Test getCallDescription method used to get the accessibility description for calls. + * For outgoing calls, we should NOT have "New Voicemail" in the description. + */ + public void testGetCallDescription_NoVoicemailOutgoing() { + PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED, + TEST_FORMATTED_NUMBER, + TEST_COUNTRY_ISO, TEST_GEOCODE, + new int[]{Calls.OUTGOING_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION); + CharSequence description = mHelper.getCallDescription(details); + assertFalse(description.toString() + .contains(this.mResources.getString(R.string.description_new_voicemail))); + } + + /** + * Test getCallDescription method used to get the accessibility description for calls. + * For regular incoming calls, we should NOT have "New Voicemail" in the description. + */ + public void testGetCallDescription_NoVoicemailIncoming() { + PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED, + TEST_FORMATTED_NUMBER, + TEST_COUNTRY_ISO, TEST_GEOCODE, + new int[]{Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION); + CharSequence description = mHelper.getCallDescription(details); + assertFalse(description.toString() + .contains(this.mResources.getString(R.string.description_new_voicemail))); + } + + /** + * Test getCallDescription method used to get the accessibility description for calls. + * For regular missed calls, we should NOT have "New Voicemail" in the description. + */ + public void testGetCallDescription_NoVoicemailMissed() { + PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED, + TEST_FORMATTED_NUMBER, + TEST_COUNTRY_ISO, TEST_GEOCODE, + new int[]{Calls.MISSED_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION); + CharSequence description = mHelper.getCallDescription(details); + assertFalse(description.toString() + .contains(this.mResources.getString(R.string.description_new_voicemail))); + } + + /** + * Test getCallDescription method used to get the accessibility description for calls. + * For voicemail calls, we should have "New Voicemail" in the description. + */ + public void testGetCallDescription_Voicemail() { + PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED, + TEST_FORMATTED_NUMBER, + TEST_COUNTRY_ISO, TEST_GEOCODE, + new int[]{Calls.VOICEMAIL_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION); + CharSequence description = mHelper.getCallDescription(details); + assertTrue(description.toString() + .contains(this.mResources.getString(R.string.description_new_voicemail))); + } + + /** + * Test getCallDescription method used to get the accessibility description for calls. + * Test that the "X calls" message is not present if there is only a single call. + */ + public void testGetCallDescription_NumCallsSingle() { + PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED, + TEST_FORMATTED_NUMBER, + TEST_COUNTRY_ISO, TEST_GEOCODE, + new int[]{Calls.VOICEMAIL_TYPE}, TEST_DATE, TEST_DURATION); + CharSequence description = mHelper.getCallDescription(details); + + // Rather than hard coding the "X calls" string message, we'll generate it with an empty + // number of calls, and trim the resulting string. This gets us just the word "calls", + // and ensures any trivial changes to that string resource won't unnecessarily break + // the unit test. + assertFalse(description.toString() + .contains(this.mResources.getString(R.string.description_num_calls, "").trim())); + } + + /** + * Test getCallDescription method used to get the accessibility description for calls. + * Test that the "X calls" message is present if there are many calls. + */ + public void testGetCallDescription_NumCallsMultiple() { + PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED, + TEST_FORMATTED_NUMBER, + TEST_COUNTRY_ISO, TEST_GEOCODE, + new int[]{Calls.VOICEMAIL_TYPE, Calls.INCOMING_TYPE}, TEST_DATE, TEST_DURATION); + CharSequence description = mHelper.getCallDescription(details); + assertTrue(description.toString() + .contains(this.mResources.getString(R.string.description_num_calls, 2))); } - /** Asserts that the whole call area is gone. */ - private void assertNoCallButton() { - assertEquals(View.GONE, mViews.secondaryActionView.getVisibility()); + /** Asserts that the primary action view does not have a call intent. */ + private void assertNoCallIntent() { + Object intentProvider = (IntentProvider)mViews.primaryActionView.getTag(); + // The intent provider should be null as there is no ability to make a call. + assertNull(intentProvider); } /** Sets the details of a phone call using the specified phone number. */ |