1 /* 2 * Copyright (C) 2017 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.calllogutils; 18 19 import android.content.Context; 20 import android.provider.CallLog.Calls; 21 import android.text.TextUtils; 22 import com.android.dialer.calllog.model.CoalescedRow; 23 import com.android.dialer.time.Clock; 24 import com.google.common.base.Optional; 25 import com.google.common.collect.Collections2; 26 import java.util.ArrayList; 27 import java.util.List; 28 29 /** 30 * Computes the primary text and secondary text for call log entries. 31 * 32 * <p>These text values are shown in the main call log list or in the top item of the bottom sheet 33 * menu. 34 */ 35 public final class CallLogEntryText { 36 37 /** 38 * The primary text for bottom sheets is the same as shown in the entry list. 39 * 40 * <p>(In the entry list, the number of calls and additional icons are displayed as images 41 * following the primary text.) 42 */ 43 public static CharSequence buildPrimaryText(Context context, CoalescedRow row) { 44 // Always prefer the presentation name, like "Restricted". 45 Optional<String> presentationName = 46 PhoneNumberDisplayUtil.getNameForPresentation(context, row.numberPresentation()); 47 if (presentationName.isPresent()) { 48 return presentationName.get(); 49 } 50 51 // Otherwise prefer the name. 52 if (!TextUtils.isEmpty(row.numberAttributes().getName())) { 53 return row.numberAttributes().getName(); 54 } 55 56 // Otherwise prefer the formatted number. 57 if (!TextUtils.isEmpty(row.formattedNumber())) { 58 return row.formattedNumber(); 59 } 60 61 // If there's no formatted number, just return "Unknown". 62 return context.getText(R.string.new_call_log_unknown); 63 } 64 65 /** 66 * The secondary text to show in the main call log entry list. 67 * 68 * <p>Rules: 69 * 70 * <ul> 71 * <li>For numbers that are not spam or blocked: (Duo video, )?$Label|$Location Date 72 * <li>For blocked non-spam numbers: Blocked (Duo video, )?$Label|$Location Date 73 * <li>For spam but not blocked numbers: Spam (Duo video, )?$Label Date 74 * <li>For blocked spam numbers: Blocked Spam (Duo video, )?$Label Date 75 * </ul> 76 * 77 * <p>Examples: 78 * 79 * <ul> 80 * <li>Duo Video, Mobile Now 81 * <li>Duo Video 10 min ago 82 * <li>Mobile 11:45 PM 83 * <li>Mobile Sun 84 * <li>Blocked Duo Video, Mobile Now 85 * <li>Blocked Brooklyn, NJ 10 min ago 86 * <li>Spam Mobile Now 87 * <li>Spam Now 88 * <li>Blocked Spam Mobile Now 89 * <li>Brooklyn, NJ Jan 15 90 * </ul> 91 * 92 * <p>See {@link CallLogDates#newCallLogTimestampLabel(Context, long, long)} for date rules. 93 */ 94 public static CharSequence buildSecondaryTextForEntries( 95 Context context, Clock clock, CoalescedRow row) { 96 List<CharSequence> components = new ArrayList<>(); 97 98 if (row.numberAttributes().getIsBlocked()) { 99 components.add(context.getText(R.string.new_call_log_secondary_blocked)); 100 } 101 if (row.numberAttributes().getIsSpam()) { 102 components.add(context.getText(R.string.new_call_log_secondary_spam)); 103 } 104 105 components.add(getNumberTypeLabel(context, row)); 106 107 components.add( 108 CallLogDates.newCallLogTimestampLabel(context, clock.currentTimeMillis(), row.timestamp())); 109 return joinSecondaryTextComponents(components); 110 } 111 112 /** 113 * The secondary text to show in the top item of the bottom sheet. 114 * 115 * <p>This is basically the same as {@link #buildSecondaryTextForEntries(Context, Clock, 116 * CoalescedRow)} except that instead of suffixing with the time of the call, we suffix with the 117 * formatted number. 118 */ 119 public static CharSequence buildSecondaryTextForBottomSheet(Context context, CoalescedRow row) { 120 /* 121 * Rules: 122 * For numbers that are not spam or blocked: 123 * (Duo video, )?$Label|$Location [ NumberIfNoName]? 124 * For blocked non-spam numbers: 125 * Blocked (Duo video, )?$Label|$Location [ NumberIfNoName]? 126 * For spam but not blocked numbers: 127 * Spam (Duo video, )?$Label [ NumberIfNoName]? 128 * For blocked spam numbers: 129 * Blocked Spam (Duo video, )?$Label [ NumberIfNoName]? 130 * 131 * The number is shown at the end if there is no name for the entry. (It is shown in primary 132 * text otherwise.) 133 * 134 * Examples: 135 * Duo Video, Mobile 555-1234 136 * Duo Video 555-1234 137 * Mobile 555-1234 138 * Blocked Mobile 555-1234 139 * Blocked Brooklyn, NJ 555-1234 140 * Spam Mobile 555-1234 141 * Mobile 555-1234 142 * Brooklyn, NJ 143 */ 144 List<CharSequence> components = new ArrayList<>(); 145 146 if (row.numberAttributes().getIsBlocked()) { 147 components.add(context.getText(R.string.new_call_log_secondary_blocked)); 148 } 149 if (row.numberAttributes().getIsSpam()) { 150 components.add(context.getText(R.string.new_call_log_secondary_spam)); 151 } 152 153 components.add(getNumberTypeLabel(context, row)); 154 155 // If there's a presentation name, we showed it in the primary text and shouldn't show any name 156 // or number here. 157 Optional<String> presentationName = 158 PhoneNumberDisplayUtil.getNameForPresentation(context, row.numberPresentation()); 159 if (presentationName.isPresent()) { 160 return joinSecondaryTextComponents(components); 161 } 162 163 if (TextUtils.isEmpty(row.numberAttributes().getName())) { 164 // If the name is empty the number is shown as the primary text and there's nothing to add. 165 return joinSecondaryTextComponents(components); 166 } 167 if (TextUtils.isEmpty(row.formattedNumber())) { 168 // If there's no number, don't append anything. 169 return joinSecondaryTextComponents(components); 170 } 171 components.add(row.formattedNumber()); 172 return joinSecondaryTextComponents(components); 173 } 174 175 /** 176 * Returns a value such as "Duo Video, Mobile" without the time of the call or formatted number 177 * appended. 178 * 179 * <p>When the secondary text is shown in call log entry list, this prefix is suffixed with the 180 * time of the call, and when it is shown in a bottom sheet, it is suffixed with the formatted 181 * number. 182 */ 183 private static CharSequence getNumberTypeLabel(Context context, CoalescedRow row) { 184 StringBuilder secondaryText = new StringBuilder(); 185 if ((row.features() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO) { 186 // TODO(zachh): Add "Duo" prefix? 187 secondaryText.append(context.getText(R.string.new_call_log_video)); 188 } 189 String numberTypeLabel = row.numberAttributes().getNumberTypeLabel(); 190 if (!TextUtils.isEmpty(numberTypeLabel)) { 191 if (secondaryText.length() > 0) { 192 secondaryText.append(", "); 193 } 194 secondaryText.append(numberTypeLabel); 195 } else if (!row.numberAttributes().getIsSpam()) { 196 // Don't show the location if there's a number type label or the number is spam. 197 String location = row.geocodedLocation(); 198 if (!TextUtils.isEmpty(location)) { 199 if (secondaryText.length() > 0) { 200 secondaryText.append(", "); 201 } 202 secondaryText.append(location); 203 } 204 } 205 return secondaryText; 206 } 207 208 private static CharSequence joinSecondaryTextComponents(List<CharSequence> components) { 209 return TextUtils.join( 210 " ", Collections2.filter(components, (text) -> !TextUtils.isEmpty(text))); 211 } 212 } 213