Home | History | Annotate | Download | only in sms
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.messaging.sms;
     18 
     19 import android.content.Context;
     20 import android.database.Cursor;
     21 import android.net.Uri;
     22 import android.provider.BaseColumns;
     23 import android.text.TextUtils;
     24 import android.util.Patterns;
     25 
     26 import com.android.messaging.mmslib.SqliteWrapper;
     27 import com.android.messaging.util.LogUtil;
     28 
     29 import java.util.HashSet;
     30 import java.util.Set;
     31 import java.util.regex.Matcher;
     32 import java.util.regex.Pattern;
     33 
     34 /**
     35  * Utility functions for the Messaging Service
     36  */
     37 public class MmsSmsUtils {
     38     private MmsSmsUtils() {
     39         // Forbidden being instantiated.
     40     }
     41 
     42     // An alias (or commonly called "nickname") is:
     43     // Nickname must begin with a letter.
     44     // Only letters a-z, numbers 0-9, or . are allowed in Nickname field.
     45     public static boolean isAlias(final String string, final int subId) {
     46         if (!MmsConfig.get(subId).isAliasEnabled()) {
     47             return false;
     48         }
     49 
     50         final int len = string == null ? 0 : string.length();
     51 
     52         if (len < MmsConfig.get(subId).getAliasMinChars() ||
     53                 len > MmsConfig.get(subId).getAliasMaxChars()) {
     54             return false;
     55         }
     56 
     57         if (!Character.isLetter(string.charAt(0))) {    // Nickname begins with a letter
     58             return false;
     59         }
     60         for (int i = 1; i < len; i++) {
     61             final char c = string.charAt(i);
     62             if (!(Character.isLetterOrDigit(c) || c == '.')) {
     63                 return false;
     64             }
     65         }
     66 
     67         return true;
     68     }
     69 
     70     /**
     71      * mailbox         =       name-addr
     72      * name-addr       =       [display-name] angle-addr
     73      * angle-addr      =       [CFWS] "<" addr-spec ">" [CFWS]
     74      */
     75     public static final Pattern NAME_ADDR_EMAIL_PATTERN =
     76             Pattern.compile("\\s*(\"[^\"]*\"|[^<>\"]+)\\s*<([^<>]+)>\\s*");
     77 
     78     public static String extractAddrSpec(final String address) {
     79         final Matcher match = NAME_ADDR_EMAIL_PATTERN.matcher(address);
     80 
     81         if (match.matches()) {
     82             return match.group(2);
     83         }
     84         return address;
     85     }
     86 
     87     /**
     88      * Returns true if the address is an email address
     89      *
     90      * @param address the input address to be tested
     91      * @return true if address is an email address
     92      */
     93     public static boolean isEmailAddress(final String address) {
     94         if (TextUtils.isEmpty(address)) {
     95             return false;
     96         }
     97 
     98         final String s = extractAddrSpec(address);
     99         final Matcher match = Patterns.EMAIL_ADDRESS.matcher(s);
    100         return match.matches();
    101     }
    102 
    103     /**
    104      * Returns true if the number is a Phone number
    105      *
    106      * @param number the input number to be tested
    107      * @return true if number is a Phone number
    108      */
    109     public static boolean isPhoneNumber(final String number) {
    110         if (TextUtils.isEmpty(number)) {
    111             return false;
    112         }
    113 
    114         final Matcher match = Patterns.PHONE.matcher(number);
    115         return match.matches();
    116     }
    117 
    118     /**
    119      * Check if MMS is required when sending to email address
    120      *
    121      * @param destinationHasEmailAddress destination includes an email address
    122      * @return true if MMS is required.
    123      */
    124     public static boolean getRequireMmsForEmailAddress(final boolean destinationHasEmailAddress,
    125             final int subId) {
    126         if (!TextUtils.isEmpty(MmsConfig.get(subId).getEmailGateway())) {
    127             return false;
    128         } else {
    129             return destinationHasEmailAddress;
    130         }
    131     }
    132 
    133     /**
    134      * Helper functions for the "threads" table used by MMS and SMS.
    135      */
    136     public static final class Threads implements android.provider.Telephony.ThreadsColumns {
    137         private static final String[] ID_PROJECTION = { BaseColumns._ID };
    138         private static final Uri THREAD_ID_CONTENT_URI = Uri.parse(
    139                 "content://mms-sms/threadID");
    140         public static final Uri CONTENT_URI = Uri.withAppendedPath(
    141                 android.provider.Telephony.MmsSms.CONTENT_URI, "conversations");
    142 
    143         // No one should construct an instance of this class.
    144         private Threads() {
    145         }
    146 
    147         /**
    148          * This is a single-recipient version of
    149          * getOrCreateThreadId.  It's convenient for use with SMS
    150          * messages.
    151          */
    152         public static long getOrCreateThreadId(final Context context, final String recipient) {
    153             final Set<String> recipients = new HashSet<String>();
    154 
    155             recipients.add(recipient);
    156             return getOrCreateThreadId(context, recipients);
    157         }
    158 
    159         /**
    160          * Given the recipients list and subject of an unsaved message,
    161          * return its thread ID.  If the message starts a new thread,
    162          * allocate a new thread ID.  Otherwise, use the appropriate
    163          * existing thread ID.
    164          *
    165          * Find the thread ID of the same set of recipients (in
    166          * any order, without any additions). If one
    167          * is found, return it.  Otherwise, return a unique thread ID.
    168          */
    169         public static long getOrCreateThreadId(
    170                 final Context context, final Set<String> recipients) {
    171             final Uri.Builder uriBuilder = THREAD_ID_CONTENT_URI.buildUpon();
    172 
    173             for (String recipient : recipients) {
    174                 if (isEmailAddress(recipient)) {
    175                     recipient = extractAddrSpec(recipient);
    176                 }
    177 
    178                 uriBuilder.appendQueryParameter("recipient", recipient);
    179             }
    180 
    181             final Uri uri = uriBuilder.build();
    182             //if (DEBUG) Rlog.v(TAG, "getOrCreateThreadId uri: " + uri);
    183 
    184             final Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(),
    185                     uri, ID_PROJECTION, null, null, null);
    186             if (cursor != null) {
    187                 try {
    188                     if (cursor.moveToFirst()) {
    189                         return cursor.getLong(0);
    190                     } else {
    191                         LogUtil.e(LogUtil.BUGLE_DATAMODEL_TAG,
    192                                 "getOrCreateThreadId returned no rows!");
    193                     }
    194                 } finally {
    195                     cursor.close();
    196                 }
    197             }
    198 
    199             LogUtil.e(LogUtil.BUGLE_DATAMODEL_TAG, "getOrCreateThreadId failed with "
    200                     + LogUtil.sanitizePII(recipients.toString()));
    201             throw new IllegalArgumentException("Unable to find or allocate a thread ID.");
    202         }
    203     }
    204 }
    205