diff options
Diffstat (limited to 'src')
7 files changed, 271 insertions, 68 deletions
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); } } |