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