1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.dialer.calllog; 18 19 import android.content.res.Resources; 20 import android.provider.CallLog.Calls; 21 import android.text.SpannableStringBuilder; 22 import android.text.TextUtils; 23 import android.util.Log; 24 25 import com.android.dialer.PhoneCallDetails; 26 import com.android.dialer.util.AppCompatConstants; 27 import com.android.dialer.R; 28 import com.android.dialer.calllog.calllogcache.CallLogCache; 29 30 /** 31 * Helper class to fill in the views of a call log entry. 32 */ 33 /* package */class CallLogListItemHelper { 34 private static final String TAG = "CallLogListItemHelper"; 35 36 /** Helper for populating the details of a phone call. */ 37 private final PhoneCallDetailsHelper mPhoneCallDetailsHelper; 38 /** Resources to look up strings. */ 39 private final Resources mResources; 40 private final CallLogCache mCallLogCache; 41 42 /** 43 * Creates a new helper instance. 44 * 45 * @param phoneCallDetailsHelper used to set the details of a phone call 46 * @param resources The object from which resources can be retrieved 47 * @param callLogCache A cache for values retrieved from telecom/telephony 48 */ 49 public CallLogListItemHelper( 50 PhoneCallDetailsHelper phoneCallDetailsHelper, 51 Resources resources, 52 CallLogCache callLogCache) { 53 mPhoneCallDetailsHelper = phoneCallDetailsHelper; 54 mResources = resources; 55 mCallLogCache = callLogCache; 56 } 57 58 /** 59 * Sets the name, label, and number for a contact. 60 * 61 * @param views the views to populate 62 * @param details the details of a phone call needed to fill in the data 63 */ 64 public void setPhoneCallDetails( 65 CallLogListItemViewHolder views, 66 PhoneCallDetails details) { 67 mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details); 68 69 // Set the accessibility text for the contact badge 70 views.quickContactView.setContentDescription(getContactBadgeDescription(details)); 71 72 // Set the primary action accessibility description 73 views.primaryActionView.setContentDescription(getCallDescription(details)); 74 75 // Cache name or number of caller. Used when setting the content descriptions of buttons 76 // when the actions ViewStub is inflated. 77 views.nameOrNumber = getNameOrNumber(details); 78 79 // The call type or Location associated with the call. Use when setting text for a 80 // voicemail log's call button 81 views.callTypeOrLocation = mPhoneCallDetailsHelper.getCallTypeOrLocation(details); 82 83 // Cache country iso. Used for number filtering. 84 views.countryIso = details.countryIso; 85 } 86 87 /** 88 * Sets the accessibility descriptions for the action buttons in the action button ViewStub. 89 * 90 * @param views The views associated with the current call log entry. 91 */ 92 public void setActionContentDescriptions(CallLogListItemViewHolder views) { 93 if (views.nameOrNumber == null) { 94 Log.e(TAG, "setActionContentDescriptions; name or number is null."); 95 } 96 97 // Calling expandTemplate with a null parameter will cause a NullPointerException. 98 // Although we don't expect a null name or number, it is best to protect against it. 99 CharSequence nameOrNumber = views.nameOrNumber == null ? "" : views.nameOrNumber; 100 101 views.videoCallButtonView.setContentDescription( 102 TextUtils.expandTemplate( 103 mResources.getString(R.string.description_video_call_action), 104 nameOrNumber)); 105 106 views.createNewContactButtonView.setContentDescription( 107 TextUtils.expandTemplate( 108 mResources.getString(R.string.description_create_new_contact_action), 109 nameOrNumber)); 110 111 views.addToExistingContactButtonView.setContentDescription( 112 TextUtils.expandTemplate( 113 mResources.getString(R.string.description_add_to_existing_contact_action), 114 nameOrNumber)); 115 116 views.detailsButtonView.setContentDescription( 117 TextUtils.expandTemplate( 118 mResources.getString(R.string.description_details_action), nameOrNumber)); 119 } 120 121 /** 122 * Returns the accessibility description for the contact badge for a call log entry. 123 * 124 * @param details Details of call. 125 * @return Accessibility description. 126 */ 127 private CharSequence getContactBadgeDescription(PhoneCallDetails details) { 128 return mResources.getString(R.string.description_contact_details, getNameOrNumber(details)); 129 } 130 131 /** 132 * Returns the accessibility description of the "return call/call" action for a call log 133 * entry. 134 * Accessibility text is a combination of: 135 * {Voicemail Prefix}. {Number of Calls}. {Caller information} {Phone Account}. 136 * If most recent call is a voicemail, {Voicemail Prefix} is "New Voicemail.", otherwise "". 137 * 138 * If more than one call for the caller, {Number of Calls} is: 139 * "{number of calls} calls.", otherwise "". 140 * 141 * The {Caller Information} references the most recent call associated with the caller. 142 * For incoming calls: 143 * If missed call: Missed call from {Name/Number} {Call Type} {Call Time}. 144 * If answered call: Answered call from {Name/Number} {Call Type} {Call Time}. 145 * 146 * For outgoing calls: 147 * If outgoing: Call to {Name/Number] {Call Type} {Call Time}. 148 * 149 * Where: 150 * {Name/Number} is the name or number of the caller (as shown in call log). 151 * {Call type} is the contact phone number type (eg mobile) or location. 152 * {Call Time} is the time since the last call for the contact occurred. 153 * 154 * The {Phone Account} refers to the account/SIM through which the call was placed or received 155 * in multi-SIM devices. 156 * 157 * Examples: 158 * 3 calls. New Voicemail. Missed call from Joe Smith mobile 2 hours ago on SIM 1. 159 * 160 * 2 calls. Answered call from John Doe mobile 1 hour ago. 161 * 162 * @param context The application context. 163 * @param details Details of call. 164 * @return Return call action description. 165 */ 166 public CharSequence getCallDescription(PhoneCallDetails details) { 167 int lastCallType = getLastCallType(details.callTypes); 168 169 // Get the name or number of the caller. 170 final CharSequence nameOrNumber = getNameOrNumber(details); 171 172 // Get the call type or location of the caller; null if not applicable 173 final CharSequence typeOrLocation = mPhoneCallDetailsHelper.getCallTypeOrLocation(details); 174 175 // Get the time/date of the call 176 final CharSequence timeOfCall = mPhoneCallDetailsHelper.getCallDate(details); 177 178 SpannableStringBuilder callDescription = new SpannableStringBuilder(); 179 180 // Add number of calls if more than one. 181 if (details.callTypes.length > 1) { 182 callDescription.append(mResources.getString(R.string.description_num_calls, 183 details.callTypes.length)); 184 } 185 186 // If call had video capabilities, add the "Video Call" string. 187 if ((details.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO) { 188 callDescription.append(mResources.getString(R.string.description_video_call)); 189 } 190 191 String accountLabel = mCallLogCache.getAccountLabel(details.accountHandle); 192 CharSequence onAccountLabel = PhoneCallDetails.createAccountLabelDescription(mResources, 193 details.viaNumber, accountLabel); 194 195 int stringID = getCallDescriptionStringID(details.callTypes, details.isRead); 196 callDescription.append( 197 TextUtils.expandTemplate( 198 mResources.getString(stringID), 199 nameOrNumber, 200 typeOrLocation == null ? "" : typeOrLocation, 201 timeOfCall, 202 onAccountLabel)); 203 204 return callDescription; 205 } 206 207 /** 208 * Determine the appropriate string ID to describe a call for accessibility purposes. 209 * 210 * @param callTypes The type of call corresponding to this entry or multiple if this entry 211 * represents multiple calls grouped together. 212 * @param isRead If the entry is a voicemail, {@code true} if the voicemail is read. 213 * @return String resource ID to use. 214 */ 215 public int getCallDescriptionStringID(int[] callTypes, boolean isRead) { 216 int lastCallType = getLastCallType(callTypes); 217 int stringID; 218 219 if (lastCallType == AppCompatConstants.CALLS_MISSED_TYPE) { 220 //Message: Missed call from <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>, 221 //<PhoneAccount>. 222 stringID = R.string.description_incoming_missed_call; 223 } else if (lastCallType == AppCompatConstants.CALLS_INCOMING_TYPE) { 224 //Message: Answered call from <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>, 225 //<PhoneAccount>. 226 stringID = R.string.description_incoming_answered_call; 227 } else if (lastCallType == AppCompatConstants.CALLS_VOICEMAIL_TYPE) { 228 //Message: (Unread) [V/v]oicemail from <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>, 229 //<PhoneAccount>. 230 stringID = isRead ? R.string.description_read_voicemail 231 : R.string.description_unread_voicemail; 232 } else { 233 //Message: Call to <NameOrNumber>, <TypeOrLocation>, <TimeOfCall>, <PhoneAccount>. 234 stringID = R.string.description_outgoing_call; 235 } 236 return stringID; 237 } 238 239 /** 240 * Determine the call type for the most recent call. 241 * @param callTypes Call types to check. 242 * @return Call type. 243 */ 244 private int getLastCallType(int[] callTypes) { 245 if (callTypes.length > 0) { 246 return callTypes[0]; 247 } else { 248 return Calls.MISSED_TYPE; 249 } 250 } 251 252 /** 253 * Return the name or number of the caller specified by the details. 254 * @param details Call details 255 * @return the name (if known) of the caller, otherwise the formatted number. 256 */ 257 private CharSequence getNameOrNumber(PhoneCallDetails details) { 258 final CharSequence recipient; 259 if (!TextUtils.isEmpty(details.getPreferredName())) { 260 recipient = details.getPreferredName(); 261 } else { 262 recipient = details.displayNumber + details.postDialDigits; 263 } 264 return recipient; 265 } 266 } 267