• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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    */
buildPrimaryText(Context context, CoalescedRow row)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    */
buildSecondaryTextForEntries( Context context, Clock clock, CoalescedRow row)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    */
buildSecondaryTextForBottomSheet(Context context, CoalescedRow row)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    */
getNumberTypeLabel(Context context, CoalescedRow row)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 
joinSecondaryTextComponents(List<CharSequence> components)208   private static CharSequence joinSecondaryTextComponents(List<CharSequence> components) {
209     return TextUtils.join(
210         " • ", Collections2.filter(components, (text) -> !TextUtils.isEmpty(text)));
211   }
212 }
213