Как программно читать СМС-сообщения с устройства в Android?

249

Я хочу получить SMS-сообщения с устройства и отобразить их?

Манодж Перумарат
источник
@ Дэвид Фрейтас Доверенная ссылка +1
Шахзад Имам
3
@DavidFreitas эта ссылка не работает, не могли бы вы поделиться последней ссылкой?
Хобайб
3
@Khobaib, как обычно, вещи в Интернете мимолетны. Я нашел копию на archive.org stackoverflow.com/a/19966227/40961 , слава Богу за них (я недавно пожертвовал, чтобы они работали). Но мы должны рассмотреть возможность преобразования содержимого страницы из web.archive.org/web/20121022021217/http://mobdev.olin.edu/… в синтаксис уценки в ответе на этот вопрос. Наверное, часовая работа.
Дэвид д С е Фрейтас

Ответы:

157

Используйте Content Resolver ( «content: // sms / inbox» ) для чтения SMS, которые находятся в папке «Входящие».

// public static final String INBOX = "content://sms/inbox";
// public static final String SENT = "content://sms/sent";
// public static final String DRAFT = "content://sms/draft";
Cursor cursor = getContentResolver().query(Uri.parse("content://sms/inbox"), null, null, null, null);

if (cursor.moveToFirst()) { // must check the result to prevent exception
    do {
       String msgData = "";
       for(int idx=0;idx<cursor.getColumnCount();idx++)
       {
           msgData += " " + cursor.getColumnName(idx) + ":" + cursor.getString(idx);
       }
       // use msgData
    } while (cursor.moveToNext());
} else {
   // empty box, no SMS
}

Пожалуйста, добавьте разрешение READ_SMS .

Я надеюсь, что это помогает :)

Сурьявел ТР
источник
7
Спасибо! Вы неправильно написали "getColumnName", иначе он работает как шарм. Да, и если кто-то будет использовать это, не забудьте добавить разрешение android.permission.READ_SMS.
qwerty
1
Спасибо. Я изменил это :)
Suryavel TR
5
Использует ли это также недокументированный API, указанный @CommonsWare в его комментарии к принятому ответу?
Кришнабхадра
1
Внимание! Не скучай, moveToFirstкак я.
Александр Приймак
4
@ Krishnabhadra Да. Он использует недокументированный контент-провайдер "content: // sms / inbox".
pm_labs
79
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        final String myPackageName = getPackageName();
        if (!Telephony.Sms.getDefaultSmsPackage(this).equals(myPackageName)) {

            Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
            intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, myPackageName);
            startActivityForResult(intent, 1);
        }else {
            List<Sms> lst = getAllSms();
        }
    }else {
        List<Sms> lst = getAllSms();
    }

Установить приложение в качестве приложения SMS по умолчанию

    @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1) {
    if (resultCode == RESULT_OK) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            final String myPackageName = getPackageName();
            if (Telephony.Sms.getDefaultSmsPackage(mActivity).equals(myPackageName)) {

                List<Sms> lst = getAllSms();
            }
        }
    }
}
}

Функция для получения SMS

public List<Sms> getAllSms() {
    List<Sms> lstSms = new ArrayList<Sms>();
    Sms objSms = new Sms();
    Uri message = Uri.parse("content://sms/");
    ContentResolver cr = mActivity.getContentResolver();

    Cursor c = cr.query(message, null, null, null, null);
    mActivity.startManagingCursor(c);
    int totalSMS = c.getCount();

    if (c.moveToFirst()) {
        for (int i = 0; i < totalSMS; i++) {

            objSms = new Sms();
            objSms.setId(c.getString(c.getColumnIndexOrThrow("_id")));
            objSms.setAddress(c.getString(c
                    .getColumnIndexOrThrow("address")));
            objSms.setMsg(c.getString(c.getColumnIndexOrThrow("body")));
            objSms.setReadState(c.getString(c.getColumnIndex("read")));
            objSms.setTime(c.getString(c.getColumnIndexOrThrow("date")));
            if (c.getString(c.getColumnIndexOrThrow("type")).contains("1")) {
                objSms.setFolderName("inbox");
            } else {
                objSms.setFolderName("sent");
            }

            lstSms.add(objSms);
            c.moveToNext();
        }
    }
    // else {
    // throw new RuntimeException("You have no SMS");
    // }
    c.close();

    return lstSms;
}

Класс смс ниже:

public class Sms{
private String _id;
private String _address;
private String _msg;
private String _readState; //"0" for have not read sms and "1" for have read sms
private String _time;
private String _folderName;

public String getId(){
return _id;
}
public String getAddress(){
return _address;
}
public String getMsg(){
return _msg;
}
public String getReadState(){
return _readState;
}
public String getTime(){
return _time;
}
public String getFolderName(){
return _folderName;
}


public void setId(String id){
_id = id;
}
public void setAddress(String address){
_address = address;
}
public void setMsg(String msg){
_msg = msg;
}
public void setReadState(String readState){
_readState = readState;
}
public void setTime(String time){
_time = time;
}
public void setFolderName(String folderName){
_folderName = folderName;
}

}

Не забудьте определить разрешение в вашем AndroidManifest.xml

<uses-permission android:name="android.permission.READ_SMS" />
Атиф Махмуд
источник
2
Это хороший кусок кода. Только одно, время получается в миллисекундах. Я думаю, что будет лучше сделать его читабельным для людей, например,String receiveDayTime = Functions.dateFromMilisec(Long.valueOf(c.getColumnIndexOrThrow("date")), "hh:mm a MMM dd, yyyy");
Bibaswann Bandyopadhyay
1
Какова цель сделать все с помощью getter и setter, я действительно не понимаю, почему бы просто не использовать ассоциативный массив или класс, к элементам которого обращаются напрямую
michnovka
1
@TomasNavara: проверьте этот код для понимания использования геттера и сеттера. pastebin.com/Nh8YXtyJ
Ошибки случаются
@BibaswannBandyopadhyay Если вы не хотите использовать ничего, кроме библиотек Android и Java-библиотек. new SimpleDateFormat("hh:mm", Locale.US).format(new Date(Long.parseLong(_time)));Это даст вам 24 часа времени.
Крис - младший
mActivityне определен. Что это?
dthree
61

Это тривиальный процесс. Хороший пример вы можете увидеть в исходном коде SMSPopup.

Изучите следующие методы:

SmsMmsMessage getSmsDetails(Context context, long ignoreThreadId, boolean unreadOnly)
long findMessageId(Context context, long threadId, long _timestamp, int messageType
void setMessageRead(Context context, long messageId, int messageType)
void deleteMessage(Context context, long messageId, long threadId, int messageType)

это метод для чтения:

SmsMmsMessage getSmsDetails(Context context,
                            long ignoreThreadId, boolean unreadOnly)
{
   String SMS_READ_COLUMN = "read";
   String WHERE_CONDITION = unreadOnly ? SMS_READ_COLUMN + " = 0" : null;
   String SORT_ORDER = "date DESC";
   int count = 0;
   // Log.v(WHERE_CONDITION);
   if (ignoreThreadId > 0) {
      // Log.v("Ignoring sms threadId = " + ignoreThreadId);
      WHERE_CONDITION += " AND thread_id != " + ignoreThreadId;
   }
   Cursor cursor = context.getContentResolver().query(
                      SMS_INBOX_CONTENT_URI,
                      new String[] { "_id", "thread_id", "address", "person", "date", "body" },
                      WHERE_CONDITION,
                      null,
                      SORT_ORDER);
   if (cursor != null) {
      try {
         count = cursor.getCount();
         if (count > 0) {
            cursor.moveToFirst();
            // String[] columns = cursor.getColumnNames();
            // for (int i=0; i<columns.length; i++) {
            // Log.v("columns " + i + ": " + columns[i] + ": " + cursor.getString(i));
            // }                                         
            long messageId = cursor.getLong(0);
            long threadId = cursor.getLong(1);
            String address = cursor.getString(2);
            long contactId = cursor.getLong(3);
            String contactId_string = String.valueOf(contactId);
            long timestamp = cursor.getLong(4);

            String body = cursor.getString(5);                             
            if (!unreadOnly) {
                count = 0;
            }

            SmsMmsMessage smsMessage = new SmsMmsMessage(context, address,
                          contactId_string, body, timestamp,
                          threadId, count, messageId, SmsMmsMessage.MESSAGE_TYPE_SMS);
            return smsMessage;
         }
      } finally {
         cursor.close();
      }
   }               
   return null;
}
Омер
источник
48
Это не является частью Android SDK. Этот код делает неверное предположение, что все устройства поддерживают этого недокументированного и неподдерживаемого поставщика контента. Google явно указал, что полагаться на это не очень хорошая идея: android-developers.blogspot.com/2010/05/…
CommonsWare
1
@Janusz: нет документированных и поддерживаемых средств, которые бы работали на всех клиентах SMS на всех устройствах.
CommonsWare
9
@CommonsWare, что грустно слышать. Возможно, придется жить с этим API тогда.
Януш
@Omer Есть идеи, как посчитать количество SMS-сообщений на контакт?
SpicyWeenie
4
Код переехал. Поиск SmsPopupUtils.java получил мне новую ссылку на него в коде Google. В случае, если они переместят его снова или полностью
прекратят
25

Начиная с API 19 для этого вы можете использовать класс телефонии; Так как жестко заданные значения не будут получать сообщения на всех устройствах, потому что поставщик контента Uri меняется от устройств и производителей.

public void getAllSms(Context context) {

    ContentResolver cr = context.getContentResolver();
    Cursor c = cr.query(Telephony.Sms.CONTENT_URI, null, null, null, null);
    int totalSMS = 0;
    if (c != null) {
        totalSMS = c.getCount();
        if (c.moveToFirst()) {
            for (int j = 0; j < totalSMS; j++) {
                String smsDate = c.getString(c.getColumnIndexOrThrow(Telephony.Sms.DATE));
                String number = c.getString(c.getColumnIndexOrThrow(Telephony.Sms.ADDRESS));
                String body = c.getString(c.getColumnIndexOrThrow(Telephony.Sms.BODY));
                Date dateFormat= new Date(Long.valueOf(smsDate));
                String type;
                switch (Integer.parseInt(c.getString(c.getColumnIndexOrThrow(Telephony.Sms.TYPE)))) {
                    case Telephony.Sms.MESSAGE_TYPE_INBOX:
                        type = "inbox";
                        break;
                    case Telephony.Sms.MESSAGE_TYPE_SENT:
                        type = "sent";
                        break;
                    case Telephony.Sms.MESSAGE_TYPE_OUTBOX:
                        type = "outbox";
                        break;
                    default:
                        break;
                }


                c.moveToNext();
            }
        }

        c.close();

    } else {
        Toast.makeText(this, "No message to show!", Toast.LENGTH_SHORT).show();
    }
}
Манодж Перумарат
источник
9
Кажется, что это единственный ответ, который не использует недокументированный API и не относится к сторонним библиотекам.
Ишамаэль
Я пытался использовать этот код для получения SMS-сообщений от видеовстреч (это мое приложение SMS по умолчанию). Вместо этого он получил последнее исходящее сообщение, которое я отправил через Messenger ... Знаете ли вы, что является причиной этого?
Мики П
@MikiP, используя мои догадки, скажу, что Messenger App попросил вас заменить управление SMS на Messenger. Это происходит с другим приложением для обмена сообщениями. У меня нет другого объяснения.
m3nda
2
Не забудьте вызвать c.close ();
Сисеро Моура
1
@SardarAgabejli Если мы используем жесткие значения, такие как «contenturi: sms», это не будет одинаковым для всех устройств, но если мы используем класс телефонии, мы получим прямой доступ к этому conrint uri или пути sms db этого устройства, это класс помощника, чтобы указать на БД смс
Манодж Perumarath
23

Этот пост немного устарел, но вот еще одно простое решение для получения данных, связанных с SMSпоставщиком контента в Android:

Используйте эту библиотеку: https://github.com/EverythingMe/easy-content-providers

  • Получить все SMS:

    TelephonyProvider telephonyProvider = new TelephonyProvider(context);
    List<Sms> smses = telephonyProvider.getSms(Filter.ALL).getList();

    В каждом SMS-сообщении есть все поля, поэтому вы можете получить любую нужную вам информацию:
    адрес, тело, receiveDate, тип (INBOX, SENT, DRAFT, ..), threadId, ...

  • Гель все MMS:

    List<Mms> mmses = telephonyProvider.getMms(Filter.ALL).getList();
  • Гель все Thread:

    List<Thread> threads = telephonyProvider.getThreads().getList();
  • Гель все Conversation:

    List<Conversation> conversations = telephonyProvider.getConversations().getList();

Он работает с Listили, Cursorи есть пример приложения, чтобы увидеть, как это выглядит и работает.

На самом деле, есть поддержка всех поставщиков контента Android, таких как: Контакты, Журналы вызовов, Календарь, ... Полный документ со всеми опциями: https://github.com/EverythingMe/easy-content-providers/wiki/Android- провайдеры

Надеюсь, это также помогло :)

sromku
источник
1
Исходный код и примеры на github довольно полезны. Это хорошая обертка / фасад для большинства распространенных поставщиков. Спасибо.
m3nda
14

Шаг 1: сначала мы должны добавить разрешения в файл манифеста, как

<uses-permission android:name="android.permission.RECEIVE_SMS" android:protectionLevel="signature" />
<uses-permission android:name="android.permission.READ_SMS" />

Шаг 2: затем добавьте сервисный класс получателя смс для получения смс

<receiver android:name="com.aquadeals.seller.services.SmsReceiver">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
    </intent-filter>
</receiver>

Шаг 3: Добавить разрешение во время выполнения

private boolean checkAndRequestPermissions()
{
    int sms = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS);

    if (sms != PackageManager.PERMISSION_GRANTED)
    {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_SMS}, REQUEST_ID_MULTIPLE_PERMISSIONS);
        return false;
    }
    return true;
}

Шаг 4. Добавьте эти классы в ваше приложение и протестируйте интерфейсный класс.

public interface SmsListener {
   public void messageReceived(String messageText);
}

SmsReceiver.java

public class SmsReceiver extends BroadcastReceiver {
private static SmsListener mListener;
public Pattern p = Pattern.compile("(|^)\\d{6}");
@Override
public void onReceive(Context context, Intent intent) {
    Bundle data  = intent.getExtras();
    Object[] pdus = (Object[]) data.get("pdus");
    for(int i=0;i<pdus.length;i++)
    {
        SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdus[i]);
        String sender = smsMessage.getDisplayOriginatingAddress();
        String phoneNumber = smsMessage.getDisplayOriginatingAddress();
        String senderNum = phoneNumber ;
        String messageBody = smsMessage.getMessageBody();
        try
        {
  if(messageBody!=null){
   Matcher m = p.matcher(messageBody);
    if(m.find()) {
      mListener.messageReceived(m.group(0));  }
 else {}}  }
        catch(Exception e){} } }
public static void bindListener(SmsListener listener) {
    mListener = listener; }}
Venkatesh
источник
Что делает шаблон?
Марк Буйкема
Хорошо ... это ("com.aquadeals.seller.services.SmsReceiver") общее имя службы?
m3nda
Да, это не имя службы, это путь к классу SmsReceiver в моем приложении
Venkatesh
Зачем нужно разрешение на РАСПОЛОЖЕНИЕ?
Zam Sunk
1
я пытаюсь сделать приложение, которое выскакивает смс-контент пользователю, даже если приложение было убито
Anjani Mittal
11

Есть много уже доступных ответов, но я думаю, что все они упускают важную часть этого вопроса. Прежде чем читать данные из внутренней базы данных или ее таблицы, мы должны понять, как в ней хранятся данные, а затем найти решение вышеуказанного вопроса:

Как программно читать СМС-сообщения с устройства в Android?

Итак, в андроид смс таблица выглядит так

введите описание изображения здесь

Знайте, мы можем выбрать все, что мы хотим из базы данных. В нашем случае нам нужно только

идентификатор, адрес и тело

В случае чтения СМС:

1. Спросите разрешения

int REQUEST_PHONE_CALL = 1;

   if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_SMS}, REQUEST_PHONE_CALL);
        }

или

 <uses-permission android:name="android.permission.READ_SMS" />

2. Теперь ваш код выглядит так

// Create Inbox box URI
Uri inboxURI = Uri.parse("content://sms/inbox");

// List required columns
String[] reqCols = new String[]{"_id", "address", "body"};

// Get Content Resolver object, which will deal with Content Provider
ContentResolver cr = getContentResolver();

// Fetch Inbox SMS Message from Built-in Content Provider
Cursor c = cr.query(inboxURI, reqCols, null, null, null);

// Attached Cursor with adapter and display in listview
adapter = new SimpleCursorAdapter(this, R.layout.a1_row, c,
        new String[]{"body", "address"}, new int[]{
        R.id.A1_txt_Msg, R.id.A1_txt_Number});
lst.setAdapter(adapter);

Я надеюсь, что это будет полезно. Спасибо.

Нитин Ханна
источник
7

Сервисы Google Play имеют два API, которые вы можете использовать для упрощения процесса проверки на основе SMS

SMS Retriever API

Предоставляет полностью автоматизированный пользовательский интерфейс, не требуя от пользователя ручного ввода кодов проверки и не требуя каких-либо дополнительных разрешений приложения, и должен использоваться по возможности. Однако он требует, чтобы вы поместили пользовательский хеш-код в тело сообщения, поэтому вы должны также контролировать серверную часть .

  • Требования к сообщениям - 11-значный хэш-код, который однозначно идентифицирует ваше приложение
  • Требования к отправителю - Нет
  • Взаимодействие с пользователем - нет

Запросить SMS-подтверждение в приложении для Android

Выполните проверку SMS на сервере

API согласия пользователя SMS

Не требует настраиваемого хеш-кода, однако требует, чтобы пользователь одобрил запрос вашего приложения на доступ к сообщению, содержащему проверочный код. Чтобы свести к минимуму вероятность появления неправильного сообщения для пользователя, SMS User Consentбудет отфильтровываться сообщения от отправителей в списке контактов пользователя.

  • Требования к сообщению - 4-10-значный буквенно-цифровой код, содержащий не менее одного номера
  • Требования к отправителю - отправитель не может быть в списке контактов пользователя
  • Взаимодействие с пользователем - одно нажатие для подтверждения

The SMS User Consent APIявляется частью Сервисов Google Play. Для его использования вам понадобится как минимум версия 17.0.0этих библиотек:

implementation "com.google.android.gms:play-services-auth:17.0.0"
implementation "com.google.android.gms:play-services-auth-api-phone:17.1.0"

Шаг 1: Начните слушать SMS-сообщения

SMS User Consent будет прослушивать входящие SMS-сообщения, содержащие одноразовый код, до пяти минут. Он не будет проверять сообщения, отправленные до его запуска. Если вам известен номер телефона, на который будет отправляться одноразовый код, вы можете указать senderPhoneNumber, или, если вы не nullсоответствуете ни одному номеру.

 smsRetriever.startSmsUserConsent(senderPhoneNumber /* or null */)

Шаг 2: Запрос согласия на чтение сообщения

Как только ваше приложение получит сообщение, содержащее одноразовый код, оно будет уведомлено о трансляции. На данный момент у вас нет согласия на чтение сообщения - вместо этого вы получаетеIntent что вы можете начать запрашивать согласие пользователя. Внутри вашего BroadcastReceiver, вы показываете подсказку, используя Intentв extras. Когда вы запускаете это намерение, он запрашивает у пользователя разрешение на чтение одного сообщения. Им будет показан весь текст, которым они поделятся с вашим приложением.

val consentIntent = extras.getParcelable<Intent>(SmsRetriever.EXTRA_CONSENT_INTENT)
startActivityForResult(consentIntent, SMS_CONSENT_REQUEST)

введите описание изображения здесь

Шаг 3: Разбор одноразового кода и полное подтверждение SMS

Когда пользователь нажимает “Allow”- самое время прочитать сообщение! Внутри onActivityResultвы можете получить полный текст SMS-сообщения из данных:

val message = data.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE)

Затем вы анализируете SMS-сообщение и передаете одноразовый код своему бэкэнду!

Левон Петросян
источник
4-10 digit alphanumeric code containing at least one numberМожете ли вы объяснить, что это значит? Означает ли это, что длина всего сообщения должна составлять 4-10 символов только из SMS-кода?
Зеешан Шаббир
Спасибо и вам
Левон Петросян
Это работает только для проверки OTP, верно? Как насчет чтения всех других сообщений внутри телефона, всех SMS и т. Д.? Есть ли новый API для этого, пожалуйста, дайте мне знать. Удачного кодирования! :)
Manoj Perumarath
У нас всегда есть ошибка тайм-аута. Пожалуйста, помогите мне
Manikandan K
2
String WHERE_CONDITION = unreadOnly ? SMS_READ_COLUMN + " = 0" : null;

изменено:

String WHERE_CONDITION = unreadOnly ? SMS_READ_COLUMN + " = 0 " : SMS_READ_COLUMN + " = 1 ";
Ван Хау Хоанг
источник
2

Код Котлина для чтения смс:

1- Добавьте это разрешение в AndroidManifest.xml:

    <uses-permission android:name="android.permission.RECEIVE_SMS"/>

2-Создать класс BroadCastreceiver:

package utils.broadcastreceivers

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.telephony.SmsMessage
import android.util.Log

class MySMSBroadCastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
    var body = ""
    val bundle = intent?.extras
    val pdusArr = bundle!!.get("pdus") as Array<Any>
    var messages: Array<SmsMessage?>  = arrayOfNulls(pdusArr.size)

 // if SMSis Long and contain more than 1 Message we'll read all of them
    for (i in pdusArr.indices) {
        messages[i] = SmsMessage.createFromPdu(pdusArr[i] as ByteArray)
    }
      var MobileNumber: String? = messages[0]?.originatingAddress
       Log.i(TAG, "MobileNumber =$MobileNumber")         
       val bodyText = StringBuilder()
        for (i in messages.indices) {
            bodyText.append(messages[i]?.messageBody)
        }
        body = bodyText.toString()
        if (body.isNotEmpty()){
       // Do something, save SMS in DB or variable , static object or .... 
                       Log.i("Inside Receiver :" , "body =$body")
        }
    }
 }

3-Получить SMS-разрешение, если Android 6 и выше:

   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && 
    ActivityCompat.checkSelfPermission(context!!,
            Manifest.permission.RECEIVE_SMS
        ) != PackageManager.PERMISSION_GRANTED
    ) { // Needs permission

            requestPermissions(arrayOf(Manifest.permission.RECEIVE_SMS),
            PERMISSIONS_REQUEST_READ_SMS
        )

    } else { // Permission has already been granted

    }

4- Добавьте этот код запроса в Activity или фрагмент:

 companion object {
    const val PERMISSIONS_REQUEST_READ_SMS = 100
   }

5- Переопределение проверки разрешений. Результат запроса:

 override fun onRequestPermissionsResult(
    requestCode: Int, permissions: Array<out String>,
    grantResults: IntArray
) {
    when (requestCode) {

        PERMISSIONS_REQUEST_READ_SMS -> {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Log.i("BroadCastReceiver", "PERMISSIONS_REQUEST_READ_SMS Granted")
            } else {
                //  toast("Permission must be granted  ")
            }
        }
    }
}
Хамед Джалилиани
источник
1

Самая простая функция

Чтобы прочитать смс, я написал функцию, которая возвращает объект беседы:

class Conversation(val number: String, val message: List<Message>)
class Message(val number: String, val body: String, val date: Date)

fun getSmsConversation(context: Context, number: String? = null, completion: (conversations: List<Conversation>?) -> Unit) {
        val cursor = context.contentResolver.query(Telephony.Sms.CONTENT_URI, null, null, null, null)

        val numbers = ArrayList<String>()
        val messages = ArrayList<Message>()
        var results = ArrayList<Conversation>()

        while (cursor != null && cursor.moveToNext()) {
            val smsDate = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.DATE))
            val number = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.ADDRESS))
            val body = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.BODY))

            numbers.add(number)
            messages.add(Message(number, body, Date(smsDate.toLong())))
        }

        cursor?.close()

        numbers.forEach { number ->
            if (results.find { it.number == number } == null) {
                val msg = messages.filter { it.number == number }
                results.add(Conversation(number = number, message = msg))
            }
        }

        if (number != null) {
            results = results.filter { it.number == number } as ArrayList<Conversation>
        }

        completion(results)
    }

С помощью:

getSmsConversation(this){ conversations ->
    conversations.forEach { conversation ->
        println("Number: ${conversation.number}")
        println("Message One: ${conversation.message[0].body}")
        println("Message Two: ${conversation.message[1].body}")
    }
}

Или получить только разговор на конкретный номер:

getSmsConversation(this, "+33666494128"){ conversations ->
    conversations.forEach { conversation ->
        println("Number: ${conversation.number}")
        println("Message One: ${conversation.message[0].body}")
        println("Message Two: ${conversation.message[1].body}")
    }
}
Микаэль Белхассен
источник