Пишем приложение для чтения входящих сообщений (TTS – Text-to-Speech)
Пишем приложение для чтения входящих сообщений (TTS – Text-to-Speech)
Главная » Пишем приложение для чтения входящих сообщений (TTS – Text-to-Speech)

Такая штука, как голосовые сообщения, голосовая почта, управление поиском с помощью голоса, известны уже довольно давно. Но, не взирая на это, использование подобных инструментов еще не стало реально широко используемым. И сегодня мы возьмемся за исправление этого упущения – создадим приложение, которое будет читать вслух входящие сообщения. Согласитесь, звучит довольно интересно. Итак, попробуем воплотить идею в жизнь с помощью Android Studio.

Создаем новый проект, минимальную версию Android выставляем 2.3. Создаем пустую активность Empty Activity.

Нашему будущему приложению нужно получить 3 разрешения в файле манифеста:

     - RECEIVE_SMS – для приема смс сообщений;

     - READ_SMS – для чтения смс сообщений;

     - READ_CONTACTS – для  отображения имени того, чье сообщение пришло.

Добавим в AndroidManifest.xml следующие строки:

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

Наше приложение будет иметь только портретную ориентацию, поэтому объявим об этом в теге <activity>

android:screenOrientation="portrait"

На этом работа с файлом AndroidManifest.xml закончена. 

Теперь отредактируем файл strings.xml, добавив туда следующие строки:

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <string name="app_name">SMSReader</string>
 <string name="sms_label">Latest SMS</string>
 <string name="none">None</string>
 <string name="speech_toggle_on">START SPEAKING</string>
 <string name="speech_toggle_off">STOP SPEAKING</string>
 
 <string name="start_speaking">Okay! I will read your messages out loud for you now.</string>
 <string name="stop_speaking">Okay! I will stay silent now.</string>
</resources>

Теперь возьмемся за настройку внешнего вида нашего приложения. Открываем файл интерфейса activity_main.xml. Нам нужно добавить элементы:

     - TextView -  для отображения имени того, чья последняя смс-ка к нам прилетела;

     - TextView -  для отображения содержимого последней смс;

     - ToggleButton - переключатель для включения и выключения чтения сообщений.

После добавления необходимых элементов наш layout файл имеет вид:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 tools:context="${packageName}.${activityClass}"
 android:background="#99CC00"
 >
 
 <TextView
 android:id="@+id/sms_sender"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_alignParentTop="true"
 android:layout_centerHorizontal="true"
 android:layout_marginTop="20dp"
 android:text="@string/sms_label"
 android:textColor="#ffffff"
 android:textAppearance="?android:attr/textAppearanceSmall" />
 
 <TextView
 android:id="@+id/sms_text"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_below="@+id/sms_sender"
 android:layout_centerHorizontal="true"
 android:layout_marginTop="10dp"
 android:text="@string/none"
 android:textAppearance="?android:attr/textAppearanceLarge"
 android:textColor="#ffffff"
 />
 
 <ToggleButton
 android:id="@+id/speechToggle"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_centerHorizontal="true"
 android:layout_centerVertical="true"
 android:textOff="@string/speech_toggle_on"
 android:textOn="@string/speech_toggle_off" 
 />
 
</RelativeLayout>

Теперь создадим новый класс для настройки нашего говоруна. Создаем новый java класс Speaker.java. Он будет использоваться для того, чтобы избежать запуска TTS API вместе с основным Activity. Этот класс будет осуществлять OnInitListener интерфейс, который будет сообщать, что TTS готов к использованию. Мы запишем этот показатель готовности к работе как логическую переменную с именем ready. Также зададим еще одну логическую переменную allowed, которая принимает значение true когда пользователь разрешил TTS проговаривать сообщения. Также мы создадим методы для получения и настройки значений этих переменных. Итак, если все сказанное ввести в наш класс, он примет вот такой вид:

public class Speaker implements OnInitListener {
 
 private TextToSpeech tts;
 
 private boolean ready = false;
 
 private boolean allowed = false;
 
 public Speaker(Context context){
 tts = new TextToSpeech(context, this); 
 } 
 
 public boolean isAllowed(){
 return allowed;
 }
 
 public void allow(boolean allowed){
 this.allowed = allowed;
 }
}

Интерфейс OnInitListener имеет всего один метод – onInit. Этот метод выполняется когда TTS инициализирован. Параметр Status позволяет нам узнать, как прошла инициализация. Когда подтверждается, что инициализация прошла успешно, мы должны настроить нужный язык для работы TTS. Поэтому добавляем этот метод:

@Override
public void onInit(int status) {
 if(status == TextToSpeech.SUCCESS){
 //Применяем это, чтобы выбрать локализацию
 tts.setLanguage(Locale.US);
 ready = true;
 }else{
 ready = false;
 }
}

Далее мы добавляем метод speak, который и будет читать сообщения, когда это возможно. Перед тем, как начать чтение, метод проверяет значения введенных нами переменных allowed и ready, имеют ли они значение true.  Генерируемая речь размещается в потоковом уведомлении:

public void speak(String text){
 
 //Речь начнется, только если TTS уже готова к использованию
 //и пользователь позволил ей работать:
 if(ready && allowed) {
 HashMap<String, String> hash = new HashMap<String,String>();
 hash.put(TextToSpeech.Engine.KEY_PARAM_STREAM, 
 String.valueOf(AudioManager.STREAM_NOTIFICATION));
 tts.speak(text, TextToSpeech.QUEUE_ADD, hash);
 }
}

Также нам нужно добавить метод, который будет создавать нам определенную задержку между проигрыванием слов. Мы будем использовать этот метод для того, чтобы сделать чтение программой текста более чистым. Добавим для этого новый метод:

public void pause(int duration){
 tts.playSilence(duration, TextToSpeech.QUEUE_ADD, null);
}

Ну и, наконец, добавим метод, выключающий нашу шарманку, когда она нам не нужна, чтобы не загружать попусту оперативку устройства:

//Освобождаем оперативку устройства от лишней загрузки, когда TTS не работает:
public void destroy(){
 tts.shutdown();
}

Теперь отредактируем файл MainActivity.java. Нам нужно объявить здесь о созданных в layout элементах (TextView и ToggleView), объявить две целых величины LONG_DURATION и SHORT_DURATION, они будут использоваться в методе pause класса Speaker.java. Также объявляем целую величину CHECK_CODE, ее значение не важно, она будет использоваться в методе startActivityforResult для определения результата. Также объявляем объекты Speaker  и BroadcastReceiver. На данный момент MainActivity.java имеет вид:

public class MainActivity extends Activity { 
 
 private final int CHECK_CODE = 0x1;
 private final int LONG_DURATION = 5000;
 private final int SHORT_DURATION = 1200;
 
 private Speaker speaker; 
 
 private ToggleButton toggle;
 private OnCheckedChangeListener toggleListener;
 
 private TextView smsText;
 private TextView smsSender;
 
 private BroadcastReceiver smsReceiver;
}

Добавим метод, который будет проверять, установлен ли на устройстве TTS. Проверка выполняется использованием результата из класса Speaker.java

private void checkTTS(){
 Intent check = new Intent();
 check.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
 startActivityForResult(check, CHECK_CODE);
}

Когда результат из startActivityForResult получен, вызывается метод onActivityResult. В этом методе, если мы получаем положительный результат проверки, мы инициализируем объект Speaker, если результат отрицательный - предлагаем пользователю установить TTS. 

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 if(requestCode == CHECK_CODE){
 if(resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS){
 speaker = new Speaker(this);
 }else {
 Intent install = new Intent();
 install.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
 startActivity(install);
 }
 }
}

Теперь, чтобы установить связь с входящими сообщениями, создаем объект BroadcastReceiver. Каждый раз, когда приходит новое сообщение, вызывается метод onReceive этого объекта. Далее, используя класс SmsMessage, выполняется анализ сообщения. После этого, мы используем методы getDisplayMessageBody и  getOriginatingAddress для извлечения из проведенного анализа нужной нам информации. 

С помощью полученной благодаря проведенному анализу информации мы создаем текст, который будет зачитывать TTS.  Создаем паузу длиной LONG_DURATION перед чтением нового входящего сообщения, и паузу SHORT_DURATION между чтением имени приславшего смс и текстом самого сообщения. 

private void initializeSMSReceiver(){
 smsReceiver = new BroadcastReceiver(){
 @Override
 public void onReceive(Context context, Intent intent) {
 
 Bundle bundle = intent.getExtras();
 if(bundle!=null){
 Object[] pdus = (Object[])bundle.get("pdus");
 for(int i=0;i<pdus.length;i++){
 byte[] pdu = (byte[])pdus[i];
 SmsMessage message = SmsMessage.createFromPdu(pdu);
 String text = message.getDisplayMessageBody();
 String sender = getContactName(message.getOriginatingAddress());
 speaker.pause(LONG_DURATION);
 speaker.speak("У вас сообщение от" + sender + "!");
 speaker.pause(SHORT_DURATION);
 speaker.speak(text);
 smsSender.setText("Сообщение от " + sender);
 smsText.setText(text);
 }
 }
 
 } 
 }; 
}

Из входящего смс мы можем извлечь только номер отправителя, для того, чтобы узнать его имя, нужно установить связь нашего приложения с контактами устройства. С помощью следующего метода мы запрашиваем данные контактов. Если имени отправителя не будет в телефонной книге, то мы просто назначаем такому отправителю имя unknown number:

private String getContactName(String phone){
 Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phone));
 String projection[] = new String[]{ContactsContract.Data.DISPLAY_NAME};
 Cursor cursor = getContentResolver().query(uri, projection, null, null, null); 
 if(cursor.moveToFirst()){
 return cursor.getString(0);
 }else {
 return "Неизвестный номер";
 }
}

Перед использованием BroadcastReceiver его нужно зарегистрировать. Создаем фильтр для входящих текстовых сообщений IntentFilter и регистрируем smsReceiver:

private void registerSMSReceiver() { 
 IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
 registerReceiver(smsReceiver, intentFilter);
}

Пришло время поработать над методом onCreate. Здесь мы инициализируем все объявленные объекты. Также инициализируем toggleListener для настройки значения allowed из класса Speaker.java

После инициализации вызываем методы checkTTS, initializeSMSReceiver, registerSMSReceiver:

@Override
protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main); 
 
 toggle = (ToggleButton)findViewById(R.id.speechToggle); 
 smsText = (TextView)findViewById(R.id.sms_text);
 smsSender = (TextView)findViewById(R.id.sms_sender);
 
 toggleListener = new OnCheckedChangeListener() { 
 @Override
 public void onCheckedChanged(CompoundButton view, boolean isChecked) {
 if(isChecked){
 speaker.allow(true);
 speaker.speak(getString(R.string.start_speaking));
 }else{
 speaker.speak(getString(R.string.stop_speaking));
 speaker.allow(false); 
 }
 }
 }; 
 toggle.setOnCheckedChangeListener(toggleListener);
 
 checkTTS();
 initializeSMSReceiver();
 registerSMSReceiver();
} 

Ну и под конец, используем метод onDestroy для отключения нашего TTS, чтобы он не занимал попусту ресурсы, когда мы его не используем:

@Override
protected void onDestroy() { 
 super.onDestroy();
 unregisterReceiver(smsReceiver);
 speaker.destroy();
}

Ну вот мы и закончили приложение! Теперь, чтобы его протестировать, установите программу на смартфон, активируйте возможность чтения сообщений тапом по ToggleButton и пришлите себе с другого устройства сообщение смс, либо попросите сделать это друга (можно еще сидеть и ждать смс от оператора:)) и убедитесь в работоспособности или не работоспособности приложения.

Также предлагаю ознакомиться с уроком по реализации преобразования голоса в текст. Удачи!

Оригинал статьи здесь.

Категория: Уроки программирования | Просмотров: 1182 | Добавил: Oleg | Рейтинг: 0.0/0
Всего комментариев: 0
avatar