214 lines
8.5 KiB
Java
214 lines
8.5 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.assisteddialing;
|
|
|
|
import android.content.Context;
|
|
import android.support.annotation.NonNull;
|
|
import android.telephony.PhoneNumberUtils;
|
|
import android.text.TextUtils;
|
|
import com.android.dialer.common.LogUtil;
|
|
import com.android.dialer.logging.DialerImpression;
|
|
import com.android.dialer.logging.Logger;
|
|
import com.android.dialer.phonenumberutil.PhoneNumberHelper;
|
|
import com.android.dialer.strictmode.StrictModeUtils;
|
|
import com.google.i18n.phonenumbers.NumberParseException;
|
|
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
|
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
|
|
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber.CountryCodeSource;
|
|
import java.util.Locale;
|
|
import java.util.Optional;
|
|
|
|
/** Ensures that a number is eligible for Assisted Dialing */
|
|
final class Constraints {
|
|
private final PhoneNumberUtil phoneNumberUtil;
|
|
private final Context context;
|
|
private final CountryCodeProvider countryCodeProvider;
|
|
|
|
/**
|
|
* Create a new instance of Constraints.
|
|
*
|
|
* @param context The context used to determine whether or not a number is an emergency number.
|
|
* @param countryCodeProvider A csv of supported country codes, e.g. "US,CA"
|
|
*/
|
|
public Constraints(@NonNull Context context, @NonNull CountryCodeProvider countryCodeProvider) {
|
|
if (context == null) {
|
|
throw new NullPointerException("Provided context cannot be null");
|
|
}
|
|
this.context = context;
|
|
|
|
if (countryCodeProvider == null) {
|
|
throw new NullPointerException("Provided configProviderCountryCodes cannot be null");
|
|
}
|
|
|
|
this.countryCodeProvider = countryCodeProvider;
|
|
this.phoneNumberUtil = StrictModeUtils.bypass(() -> PhoneNumberUtil.getInstance());
|
|
}
|
|
|
|
/**
|
|
* Determines whether or not we think Assisted Dialing is possible given the provided parameters.
|
|
*
|
|
* @param numberToCheck A string containing the phone number.
|
|
* @param userHomeCountryCode A string containing an ISO 3166-1 alpha-2 country code representing
|
|
* the user's home country.
|
|
* @param userRoamingCountryCode A string containing an ISO 3166-1 alpha-2 country code
|
|
* representing the user's roaming country.
|
|
* @return A boolean indicating whether or not the provided values are eligible for assisted
|
|
* dialing.
|
|
*/
|
|
boolean meetsPreconditions(
|
|
@NonNull String numberToCheck,
|
|
@NonNull String userHomeCountryCode,
|
|
@NonNull String userRoamingCountryCode) {
|
|
|
|
if (TextUtils.isEmpty(numberToCheck)) {
|
|
LogUtil.i("Constraints.meetsPreconditions", "numberToCheck was empty");
|
|
return false;
|
|
}
|
|
|
|
if (TextUtils.isEmpty(userHomeCountryCode)) {
|
|
LogUtil.i("Constraints.meetsPreconditions", "userHomeCountryCode was empty");
|
|
return false;
|
|
}
|
|
|
|
if (TextUtils.isEmpty(userRoamingCountryCode)) {
|
|
LogUtil.i("Constraints.meetsPreconditions", "userRoamingCountryCode was empty");
|
|
return false;
|
|
}
|
|
|
|
userHomeCountryCode = userHomeCountryCode.toUpperCase(Locale.US);
|
|
userRoamingCountryCode = userRoamingCountryCode.toUpperCase(Locale.US);
|
|
|
|
Optional<PhoneNumber> parsedPhoneNumber = parsePhoneNumber(numberToCheck, userHomeCountryCode);
|
|
|
|
if (!parsedPhoneNumber.isPresent()) {
|
|
LogUtil.i("Constraints.meetsPreconditions", "parsedPhoneNumber was empty");
|
|
return false;
|
|
}
|
|
|
|
return areSupportedCountryCodes(userHomeCountryCode, userRoamingCountryCode)
|
|
&& isUserRoaming(userHomeCountryCode, userRoamingCountryCode)
|
|
&& isNotInternationalNumber(parsedPhoneNumber)
|
|
&& isNotEmergencyNumber(numberToCheck, context)
|
|
&& isValidNumber(parsedPhoneNumber)
|
|
&& doesNotHaveExtension(parsedPhoneNumber);
|
|
}
|
|
|
|
/** Returns a boolean indicating the value equivalence of the provided country codes. */
|
|
private boolean isUserRoaming(
|
|
@NonNull String userHomeCountryCode, @NonNull String userRoamingCountryCode) {
|
|
boolean result = !userHomeCountryCode.equals(userRoamingCountryCode);
|
|
LogUtil.i("Constraints.isUserRoaming", String.valueOf(result));
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Returns a boolean indicating the support of both provided country codes for assisted dialing.
|
|
* Both country codes must be allowed for the return value to be true.
|
|
*/
|
|
private boolean areSupportedCountryCodes(
|
|
@NonNull String userHomeCountryCode, @NonNull String userRoamingCountryCode) {
|
|
if (TextUtils.isEmpty(userHomeCountryCode)) {
|
|
LogUtil.i("Constraints.areSupportedCountryCodes", "userHomeCountryCode was empty");
|
|
return false;
|
|
}
|
|
|
|
if (TextUtils.isEmpty(userRoamingCountryCode)) {
|
|
LogUtil.i("Constraints.areSupportedCountryCodes", "userRoamingCountryCode was empty");
|
|
return false;
|
|
}
|
|
|
|
boolean result =
|
|
countryCodeProvider.isSupportedCountryCode(userHomeCountryCode)
|
|
&& countryCodeProvider.isSupportedCountryCode(userRoamingCountryCode);
|
|
LogUtil.i("Constraints.areSupportedCountryCodes", String.valueOf(result));
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* A convenience method to take a number as a String and a specified country code, and return a
|
|
* PhoneNumber object.
|
|
*/
|
|
private Optional<PhoneNumber> parsePhoneNumber(
|
|
@NonNull String numberToParse, @NonNull String userHomeCountryCode) {
|
|
return StrictModeUtils.bypass(
|
|
() -> {
|
|
try {
|
|
return Optional.of(
|
|
phoneNumberUtil.parseAndKeepRawInput(numberToParse, userHomeCountryCode));
|
|
} catch (NumberParseException e) {
|
|
Logger.get(context)
|
|
.logImpression(DialerImpression.Type.ASSISTED_DIALING_CONSTRAINT_PARSING_FAILURE);
|
|
LogUtil.i("Constraints.parsePhoneNumber", "could not parse the number");
|
|
return Optional.empty();
|
|
}
|
|
});
|
|
}
|
|
|
|
/** Returns a boolean indicating if the provided number is already internationally formatted. */
|
|
private boolean isNotInternationalNumber(@NonNull Optional<PhoneNumber> parsedPhoneNumber) {
|
|
|
|
if (parsedPhoneNumber.get().hasCountryCode()
|
|
&& parsedPhoneNumber.get().getCountryCodeSource()
|
|
!= CountryCodeSource.FROM_DEFAULT_COUNTRY) {
|
|
Logger.get(context)
|
|
.logImpression(DialerImpression.Type.ASSISTED_DIALING_CONSTRAINT_NUMBER_HAS_COUNTRY_CODE);
|
|
LogUtil.i(
|
|
"Constraints.isNotInternationalNumber", "phone number already provided the country code");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns a boolean indicating if the provided number has an extension.
|
|
*
|
|
* <p>Extensions are currently stripped when formatting a number for mobile dialing, so we don't
|
|
* want to purposefully truncate a number.
|
|
*/
|
|
private boolean doesNotHaveExtension(@NonNull Optional<PhoneNumber> parsedPhoneNumber) {
|
|
|
|
if (parsedPhoneNumber.get().hasExtension()
|
|
&& !TextUtils.isEmpty(parsedPhoneNumber.get().getExtension())) {
|
|
Logger.get(context)
|
|
.logImpression(DialerImpression.Type.ASSISTED_DIALING_CONSTRAINT_NUMBER_HAS_EXTENSION);
|
|
LogUtil.i("Constraints.doesNotHaveExtension", "phone number has an extension");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** Returns a boolean indicating if the provided number is considered to be a valid number. */
|
|
private boolean isValidNumber(@NonNull Optional<PhoneNumber> parsedPhoneNumber) {
|
|
boolean result =
|
|
StrictModeUtils.bypass(() -> phoneNumberUtil.isValidNumber(parsedPhoneNumber.get()));
|
|
LogUtil.i("Constraints.isValidNumber", String.valueOf(result));
|
|
|
|
return result;
|
|
}
|
|
|
|
/** Returns a boolean indicating if the provided number is an emergency number. */
|
|
private boolean isNotEmergencyNumber(@NonNull String numberToCheck, @NonNull Context context) {
|
|
// isEmergencyNumber may depend on network state, so also use isLocalEmergencyNumber when
|
|
// roaming and out of service.
|
|
boolean result =
|
|
!PhoneNumberUtils.isEmergencyNumber(numberToCheck)
|
|
&& !PhoneNumberHelper.isLocalEmergencyNumber(context, numberToCheck);
|
|
LogUtil.i("Constraints.isNotEmergencyNumber", String.valueOf(result));
|
|
return result;
|
|
}
|
|
}
|