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