291 lines
11 KiB
Java
291 lines
11 KiB
Java
|
/*
|
||
|
* 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.
|
||
|
*
|
||
|
* <p>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.
|
||
|
*
|
||
|
* <p>(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<String> 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.
|
||
|
*
|
||
|
* <p>This method first obtains a list of strings to be shown in order and then concatenates them
|
||
|
* with " • ".
|
||
|
*
|
||
|
* <p>Examples:
|
||
|
*
|
||
|
* <ul>
|
||
|
* <li>Mobile, Duo video • 10 min ago
|
||
|
* <li>Spam • Mobile • Now
|
||
|
* <li>Blocked • Spam • Mobile • Now
|
||
|
* </ul>
|
||
|
*
|
||
|
* @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.
|
||
|
*
|
||
|
* <p>Rules:
|
||
|
*
|
||
|
* <ul>
|
||
|
* <li>An emergency number: [{Date}]
|
||
|
* <li>Number - not blocked, call - not spam:
|
||
|
* <p>[{$Label(, Duo video|Carrier video)?|$Location}, {Date}]
|
||
|
* <li>Number - blocked, call - not spam:
|
||
|
* <p>["Blocked", {$Label(, Duo video|Carrier video)?|$Location}, {Date}]
|
||
|
* <li>Number - not blocked, call - spam:
|
||
|
* <p>["Spam", {$Label(, Duo video|Carrier video)?}, {Date}]
|
||
|
* <li>Number - blocked, call - spam:
|
||
|
* <p>["Blocked, Spam", {$Label(, Duo video|Carrier video)?}, {Date}]
|
||
|
* </ul>
|
||
|
*
|
||
|
* <p>Examples:
|
||
|
*
|
||
|
* <ul>
|
||
|
* <li>["Mobile, Duo video", "Now"]
|
||
|
* <li>["Duo video", "10 min ago"]
|
||
|
* <li>["Mobile", "11:45 PM"]
|
||
|
* <li>["Mobile", "Sun"]
|
||
|
* <li>["Blocked", "Mobile, Duo video", "Now"]
|
||
|
* <li>["Blocked", "Brooklyn, NJ", "10 min ago"]
|
||
|
* <li>["Spam", "Mobile", "Now"]
|
||
|
* <li>["Spam", "Now"]
|
||
|
* <li>["Blocked", "Spam", "Mobile", "Now"]
|
||
|
* <li>["Brooklyn, NJ", "Jan 15"]
|
||
|
* </ul>
|
||
|
*
|
||
|
* <p>See {@link CallLogDates#newCallLogTimestampLabel(Context, long, long, boolean)} for date
|
||
|
* rules.
|
||
|
*/
|
||
|
static List<CharSequence> 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<CharSequence> 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.
|
||
|
*
|
||
|
* <p>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<CharSequence> 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<String> 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.
|
||
|
*
|
||
|
* <p>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<CharSequence> components) {
|
||
|
return TextUtils.join(
|
||
|
" • ", Collections2.filter(components, (text) -> !TextUtils.isEmpty(text)));
|
||
|
}
|
||
|
}
|