/* * Copyright (C) 2015 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.messaging.sms; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.provider.BaseColumns; import android.text.TextUtils; import android.util.Patterns; import com.android.messaging.mmslib.SqliteWrapper; import com.android.messaging.util.LogUtil; import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Utility functions for the Messaging Service */ public class MmsSmsUtils { private MmsSmsUtils() { // Forbidden being instantiated. } // An alias (or commonly called "nickname") is: // Nickname must begin with a letter. // Only letters a-z, numbers 0-9, or . are allowed in Nickname field. public static boolean isAlias(final String string, final int subId) { if (!MmsConfig.get(subId).isAliasEnabled()) { return false; } final int len = string == null ? 0 : string.length(); if (len < MmsConfig.get(subId).getAliasMinChars() || len > MmsConfig.get(subId).getAliasMaxChars()) { return false; } if (!Character.isLetter(string.charAt(0))) { // Nickname begins with a letter return false; } for (int i = 1; i < len; i++) { final char c = string.charAt(i); if (!(Character.isLetterOrDigit(c) || c == '.')) { return false; } } return true; } /** * mailbox = name-addr * name-addr = [display-name] angle-addr * angle-addr = [CFWS] "<" addr-spec ">" [CFWS] */ public static final Pattern NAME_ADDR_EMAIL_PATTERN = Pattern.compile("\\s*(\"[^\"]*\"|[^<>\"]+)\\s*<([^<>]+)>\\s*"); public static String extractAddrSpec(final String address) { final Matcher match = NAME_ADDR_EMAIL_PATTERN.matcher(address); if (match.matches()) { return match.group(2); } return address; } /** * Returns true if the address is an email address * * @param address the input address to be tested * @return true if address is an email address */ public static boolean isEmailAddress(final String address) { if (TextUtils.isEmpty(address)) { return false; } final String s = extractAddrSpec(address); final Matcher match = Patterns.EMAIL_ADDRESS.matcher(s); return match.matches(); } /** * This pattern is intended for searching for carrier specific phone numbers starting with star * sign and digits such as the voice mail number. * (e.g. *20 Chile Claro) */ private static final Pattern PHONE_NUMBER_STARTING_WITH_STAR_PATTERN = Pattern.compile("\\*[0-9]+"); /** * Returns true if the number is a Phone number * * @param number the input number to be tested * @return true if number is a Phone number */ public static boolean isPhoneNumber(final String number) { if (TextUtils.isEmpty(number)) { return false; } Matcher match = Patterns.PHONE.matcher(number); if (!match.matches()) { match = PHONE_NUMBER_STARTING_WITH_STAR_PATTERN.matcher(number); return match.matches(); } return true; } /** * Check if MMS is required when sending to email address * * @param destinationHasEmailAddress destination includes an email address * @return true if MMS is required. */ public static boolean getRequireMmsForEmailAddress(final boolean destinationHasEmailAddress, final int subId) { if (!TextUtils.isEmpty(MmsConfig.get(subId).getEmailGateway())) { return false; } else { return destinationHasEmailAddress; } } /** * Helper functions for the "threads" table used by MMS and SMS. */ public static final class Threads implements android.provider.Telephony.ThreadsColumns { private static final String[] ID_PROJECTION = { BaseColumns._ID }; private static final Uri THREAD_ID_CONTENT_URI = Uri.parse( "content://mms-sms/threadID"); public static final Uri CONTENT_URI = Uri.withAppendedPath( android.provider.Telephony.MmsSms.CONTENT_URI, "conversations"); // No one should construct an instance of this class. private Threads() { } /** * This is a single-recipient version of * getOrCreateThreadId. It's convenient for use with SMS * messages. */ public static long getOrCreateThreadId(final Context context, final String recipient) { final Set recipients = new HashSet(); recipients.add(recipient); return getOrCreateThreadId(context, recipients); } /** * Given the recipients list and subject of an unsaved message, * return its thread ID. If the message starts a new thread, * allocate a new thread ID. Otherwise, use the appropriate * existing thread ID. * * Find the thread ID of the same set of recipients (in * any order, without any additions). If one * is found, return it. Otherwise, return a unique thread ID. */ public static long getOrCreateThreadId( final Context context, final Set recipients) { final Uri.Builder uriBuilder = THREAD_ID_CONTENT_URI.buildUpon(); for (String recipient : recipients) { if (isEmailAddress(recipient)) { recipient = extractAddrSpec(recipient); } uriBuilder.appendQueryParameter("recipient", recipient); } final Uri uri = uriBuilder.build(); //if (DEBUG) Rlog.v(TAG, "getOrCreateThreadId uri: " + uri); final Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(), uri, ID_PROJECTION, null, null, null); if (cursor != null) { try { if (cursor.moveToFirst()) { return cursor.getLong(0); } else { LogUtil.e(LogUtil.BUGLE_DATAMODEL_TAG, "getOrCreateThreadId returned no rows!"); } } finally { cursor.close(); } } LogUtil.e(LogUtil.BUGLE_DATAMODEL_TAG, "getOrCreateThreadId failed with " + LogUtil.sanitizePII(recipients.toString())); throw new IllegalArgumentException("Unable to find or allocate a thread ID."); } } }