Skip to content

Commit

Permalink
Support numeric short codes (only for countries ch, de, fr and us) (#587
Browse files Browse the repository at this point in the history
)
  • Loading branch information
frimtec committed Sep 20, 2024
1 parent 0f8ce5a commit ee032dd
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 25 deletions.
19 changes: 14 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,20 @@ PAssist will guide you to install this SMS adapter.
Defines the contact of your operations center sending alarms via SMS.
The SMS received from any phone number of this contact are supervised by PAssist.

PAssist also supports operations centers using alphanumeric short code SMS numbers.
As contacts in Android cannot use letters in phone numbers, such alphanumeric short codes can be
configured in the contact field "Company".
It the operations centers uses several alphanumeric short codes, they can be comma separated in the
contacts company field.
PAssist also supports operations centers using numeric and alphanumeric short code SMS numbers.
Numeric short codes are currently only supported for the following countries:

* Switzerland
* Germany
* France
* USA
Alphanumeric short codes are supported in any country.

As contacts in Android cannot contain such short code phone numbers,
they can be configured in the contact field "Company".
It the operations centers uses several short code phone numbers, they can be comma separated in the
contacts company field. Any regular phone numbers stored in the contacts field "Company" are
ignored.

![Operations center contact with two alphanumeric short code SMS numbers](images/Contacts-with-alphanumeric-short-codes.png)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,21 +64,8 @@ public boolean isContactsPhoneNumber(Contact contact, String number) {
return false;
}
ContactDao contactDao = getContactDao();
if (isAlphanumericShortCode(number)) {
return contactDao.getAlphanumericShortCodesFromContact(contact).contains(number);
}
Set<Long> contactIds = contactDao.lookupContactIdsByPhoneNumber(number);
return contactIds.contains(contact.reference().id());
}

private static boolean isAlphanumericShortCode(String phoneNumber) {
for (int i = 0; i < phoneNumber.length(); i++) {
char ch = phoneNumber.charAt(i);
if (Character.isLetter(ch)) {
return true;
}
}
return false;
return contactDao.lookupContactIdsByPhoneNumber(number).contains(contact.reference().id()) ||
contactDao.getShortCodesFromContact(contact).contains(number);
}

public Set<String> getPhoneNumbers(Contact contact) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.frimtec.android.pikettassist.service.dao;

import static com.github.frimtec.android.pikettassist.util.PhoneNumberType.fromNumber;
import static java.util.stream.Collectors.joining;

import android.content.ContentResolver;
Expand Down Expand Up @@ -34,9 +35,11 @@ public class ContactDao {
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
};

private final Context context;
private final ContentResolver contentResolver;

public ContactDao(Context context) {
this.context = context;
this.contentResolver = context.getContentResolver();
}

Expand Down Expand Up @@ -141,12 +144,12 @@ public Set<String> getPhoneNumbers(Contact contact) {
}
}

// add alphanumeric short codes from contact organization field as comma separated list
phoneNumbers.addAll(getAlphanumericShortCodesFromContact(contact));
// add short codes from contact organization field as comma separated list
phoneNumbers.addAll(getShortCodesFromContact(contact));
return phoneNumbers;
}

public Set<String> getAlphanumericShortCodesFromContact(Contact contact) {
public Set<String> getShortCodesFromContact(Contact contact) {
try (Cursor cursor = this.contentResolver.query(ContactsContract.Data.CONTENT_URI,
null, ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?", new String[]{String.valueOf(contact.reference().id()),
ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE}, null)) {
Expand All @@ -159,7 +162,7 @@ public Set<String> getAlphanumericShortCodesFromContact(Contact contact) {
return Arrays.stream(company.split(","))
.filter(Objects::nonNull)
.map(String::trim)
.filter(companyName -> companyName.length() > 0)
.filter(number -> fromNumber(number, this.context).isShortCode())
.collect(Collectors.toSet());
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.github.frimtec.android.pikettassist.service.dao.ContactDao;
import com.github.frimtec.android.pikettassist.service.system.SignalStrengthService;
import com.github.frimtec.android.pikettassist.state.ApplicationPreferences;
import com.github.frimtec.android.pikettassist.util.PhoneNumberType;
import com.takisoft.preferencex.EditTextPreference;
import com.takisoft.preferencex.PreferenceFragmentCompat;
import com.takisoft.preferencex.RingtonePreference;
Expand Down Expand Up @@ -101,7 +102,7 @@ public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String r
Contact contact = new OperationsCenterContactService(context).getOperationsCenterContact();
if (!TextUtils.isEmpty(value) && AlertConfirmMethod.valueOf(value).isSms() && contact.valid()) {
ContactDao contactDao = new ContactDao(context);
if (!contactDao.getAlphanumericShortCodesFromContact(contact).isEmpty()) {
if (contactDao.getShortCodesFromContact(contact).stream().anyMatch(number -> !PhoneNumberType.fromNumber(number, context).isSendSupport())) {
summary = summary + " / " + getString(R.string.pref_summary_send_confirm_sms);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.github.frimtec.android.pikettassist.util;

import android.content.Context;
import android.telephony.TelephonyManager;

/**
* Helper to evaluate the type of a phone number.
*
* @since 3.3.0
*/
// TODO: 20.09.2024 REPLACE when updated to com.github.frimtec:secure-sms-proxy-api version 3.3.0
public enum PhoneNumberType {
STANDARD(true, false, true),
NUMERIC_SHORT_CODE(true, true, true),
ALPHANUMERIC_SHORT_CODE(true, true, false),
EMPTY(false, false, false);

private final boolean valid;
private final boolean shortCode;
private final boolean sendSupport;

PhoneNumberType(boolean valid, boolean shortCode, boolean sendSupport) {
this.valid = valid;
this.shortCode = shortCode;
this.sendSupport = sendSupport;
}

public boolean isValid() {
return valid;
}

public boolean isShortCode() {
return shortCode;
}

public boolean isSendSupport() {
return sendSupport;
}

/**
* Returns the type of the given phone number.
*
* @param phoneNumber phone number
* @param context context to get the country code of the telephony manager, used to detect numeric short codes
* @return phone number type
**/
public static PhoneNumberType fromNumber(
String phoneNumber,
Context context
) {
return fromNumber(phoneNumber, networkCountryIso(context));
}

/**
* Returns the type of the given phone number.
*
* @param phoneNumber phone number
* @param twoLetterIsoCountryCode country code, used to detect numeric short codes
* @return phone number type
**/
public static PhoneNumberType fromNumber(
String phoneNumber,
String twoLetterIsoCountryCode
) {
if (phoneNumber == null) {
return PhoneNumberType.EMPTY;
}
String trimmedPhoneNumber = phoneNumber.trim();
if (trimmedPhoneNumber.isEmpty()) {
return PhoneNumberType.EMPTY;
}
if (isAlphanumericShortCode(trimmedPhoneNumber)) {
return PhoneNumberType.ALPHANUMERIC_SHORT_CODE;
}
if (twoLetterIsoCountryCode != null && isNumericShortCode(trimmedPhoneNumber, twoLetterIsoCountryCode.toUpperCase())) {
return PhoneNumberType.NUMERIC_SHORT_CODE;
}
return PhoneNumberType.STANDARD;
}

public static String networkCountryIso(Context context) {
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
return telephonyManager != null ? telephonyManager.getNetworkCountryIso() : "";
}

private static boolean isAlphanumericShortCode(String phoneNumber) {
for (int i = 0; i < phoneNumber.length(); i++) {
char ch = phoneNumber.charAt(i);
if (Character.isLetter(ch)) {
return true;
}
}
return false;
}

private static boolean isNumericShortCode(String phoneNumber, String countryCode) {
return switch (countryCode) {
case "CH" -> inLengthRange(phoneNumber, 3, 5);
case "FR" -> inLengthRange(phoneNumber, 5, 5);
case "DE" -> inLengthRange(phoneNumber, 4, 5);
case "US" -> phoneNumber.charAt(0) != '1' && inLengthRange(phoneNumber, 5, 6);
default -> false;
};
}

private static boolean inLengthRange(String phoneNumber, int min, int max) {
int length = phoneNumber.length();
return length >= min && length <= max;
}

}

0 comments on commit ee032dd

Please sign in to comment.