/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.dialer.calllogutils; import android.content.Context; import android.provider.CallLog.Calls; import android.telephony.PhoneNumberUtils; import android.text.TextUtils; import com.android.dialer.calllog.model.CoalescedRow; import com.android.dialer.duo.DuoComponent; import com.android.dialer.spam.Spam; import com.android.dialer.time.Clock; import com.google.common.base.Optional; import com.google.common.collect.Collections2; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Computes the primary text and secondary text for call log entries. * *

These text values are shown in the main call log list or in the top item of the bottom sheet * menu. */ public final class CallLogEntryText { /** * The primary text for bottom sheets is the same as shown in the entry list. * *

(In the entry list, the number of calls and additional icons are displayed as images * following the primary text.) */ public static CharSequence buildPrimaryText(Context context, CoalescedRow row) { // Calls to emergency services should be shown as "Emergency number". if (row.getNumberAttributes().getIsEmergencyNumber()) { return context.getText(R.string.emergency_number); } // Otherwise, follow the following order of preferences. // 1st preference: the presentation name, like "Restricted". Optional presentationName = PhoneNumberDisplayUtil.getNameForPresentation(context, row.getNumberPresentation()); if (presentationName.isPresent()) { return presentationName.get(); } // 2nd preference: the voicemail tag if the call is one made to a voicemail box. if (row.getIsVoicemailCall() && !TextUtils.isEmpty(row.getVoicemailCallTag())) { return row.getVoicemailCallTag(); } // 3rd preference: the name associated with the number. if (!TextUtils.isEmpty(row.getNumberAttributes().getName())) { return row.getNumberAttributes().getName(); } // 4th preference: the formatted number. if (!TextUtils.isEmpty(row.getFormattedNumber())) { return PhoneNumberUtils.createTtsSpannable(row.getFormattedNumber()); } // Last resort: show "Unknown". return context.getText(R.string.new_call_log_unknown); } /** * The secondary text to be shown in the main call log entry list. * *

This method first obtains a list of strings to be shown in order and then concatenates them * with " • ". * *

Examples: * *

* * @see #buildSecondaryTextListForEntries(Context, Clock, CoalescedRow, boolean) for details. */ public static CharSequence buildSecondaryTextForEntries( Context context, Clock clock, CoalescedRow row) { return joinSecondaryTextComponents( buildSecondaryTextListForEntries(context, clock, row, /* abbreviateDateTime = */ true)); } /** * Returns a list of strings to be shown in order as the main call log entry's secondary text. * *

Rules: * *

* *

Examples: * *

* *

See {@link CallLogDates#newCallLogTimestampLabel(Context, long, long, boolean)} for date * rules. */ static List buildSecondaryTextListForEntries( Context context, Clock clock, CoalescedRow row, boolean abbreviateDateTime) { // For emergency numbers, the secondary text should contain only the timestamp. if (row.getNumberAttributes().getIsEmergencyNumber()) { return Collections.singletonList( CallLogDates.newCallLogTimestampLabel( context, clock.currentTimeMillis(), row.getTimestamp(), abbreviateDateTime)); } List components = new ArrayList<>(); if (row.getNumberAttributes().getIsBlocked()) { components.add(context.getText(R.string.new_call_log_secondary_blocked)); } if (Spam.shouldShowAsSpam(row.getNumberAttributes().getIsSpam(), row.getCallType())) { components.add(context.getText(R.string.new_call_log_secondary_spam)); } components.add(getNumberTypeLabel(context, row)); components.add( CallLogDates.newCallLogTimestampLabel( context, clock.currentTimeMillis(), row.getTimestamp(), abbreviateDateTime)); return components; } /** * The secondary text to show in the top item of the bottom sheet. * *

This is basically the same as {@link #buildSecondaryTextForEntries(Context, Clock, * CoalescedRow)} except that instead of suffixing with the time of the call, we suffix with the * formatted number. */ public static CharSequence buildSecondaryTextForBottomSheet(Context context, CoalescedRow row) { /* * Rules: * For an emergency number: * Number * Number - not blocked, call - not spam: * $Label(, Duo video|Carrier video)?|$Location [• NumberIfNoName]? * Number - blocked, call - not spam: * Blocked • $Label(, Duo video|Carrier video)?|$Location [• NumberIfNoName]? * Number - not blocked, call - spam: * Spam • $Label(, Duo video|Carrier video)? [• NumberIfNoName]? * Number - blocked, call - spam: * Blocked • Spam • $Label(, Duo video|Carrier video)? [• NumberIfNoName]? * * The number is shown at the end if there is no name for the entry. (It is shown in primary * text otherwise.) * * Examples: * Mobile, Duo video • 555-1234 * Duo video • 555-1234 * Mobile • 555-1234 * Blocked • Mobile • 555-1234 * Blocked • Brooklyn, NJ • 555-1234 * Spam • Mobile • 555-1234 * Mobile • 555-1234 * Brooklyn, NJ */ // For emergency numbers, the secondary text should contain only the number. if (row.getNumberAttributes().getIsEmergencyNumber()) { return !row.getFormattedNumber().isEmpty() ? row.getFormattedNumber() : row.getNumber().getNormalizedNumber(); } List components = new ArrayList<>(); if (row.getNumberAttributes().getIsBlocked()) { components.add(context.getText(R.string.new_call_log_secondary_blocked)); } if (Spam.shouldShowAsSpam(row.getNumberAttributes().getIsSpam(), row.getCallType())) { components.add(context.getText(R.string.new_call_log_secondary_spam)); } components.add(getNumberTypeLabel(context, row)); // If there's a presentation name, we showed it in the primary text and shouldn't show any name // or number here. Optional presentationName = PhoneNumberDisplayUtil.getNameForPresentation(context, row.getNumberPresentation()); if (presentationName.isPresent()) { return joinSecondaryTextComponents(components); } if (TextUtils.isEmpty(row.getNumberAttributes().getName())) { // If the name is empty the number is shown as the primary text and there's nothing to add. return joinSecondaryTextComponents(components); } if (TextUtils.isEmpty(row.getFormattedNumber())) { // If there's no number, don't append anything. return joinSecondaryTextComponents(components); } components.add(row.getFormattedNumber()); return joinSecondaryTextComponents(components); } /** * Returns a value such as "Mobile, Duo video" without the time of the call or formatted number * appended. * *

When the secondary text is shown in call log entry list, this prefix is suffixed with the * time of the call, and when it is shown in a bottom sheet, it is suffixed with the formatted * number. */ private static CharSequence getNumberTypeLabel(Context context, CoalescedRow row) { StringBuilder secondaryText = new StringBuilder(); // The number type label comes first (e.g., "Mobile", "Work", "Home", etc). String numberTypeLabel = row.getNumberAttributes().getNumberTypeLabel(); secondaryText.append(numberTypeLabel); // Add video call info if applicable. if ((row.getFeatures() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO) { if (secondaryText.length() > 0) { secondaryText.append(", "); } boolean isDuoCall = DuoComponent.get(context).getDuo().isDuoAccount(row.getPhoneAccountComponentName()); secondaryText.append( context.getText( isDuoCall ? R.string.new_call_log_duo_video : R.string.new_call_log_carrier_video)); } // Show the location if // (1) there is no number type label, and // (2) the call should not be shown as spam. if (TextUtils.isEmpty(numberTypeLabel) && !Spam.shouldShowAsSpam(row.getNumberAttributes().getIsSpam(), row.getCallType())) { // If number attributes contain a location (obtained from a PhoneLookup), use it instead // of the one from the annotated call log. String location = !TextUtils.isEmpty(row.getNumberAttributes().getGeolocation()) ? row.getNumberAttributes().getGeolocation() : row.getGeocodedLocation(); if (!TextUtils.isEmpty(location)) { if (secondaryText.length() > 0) { secondaryText.append(", "); } secondaryText.append(location); } } return secondaryText; } private static CharSequence joinSecondaryTextComponents(List components) { return TextUtils.join( " • ", Collections2.filter(components, (text) -> !TextUtils.isEmpty(text))); } }