Статьи

Используйте Text-to-Speech на Android для чтения входящих сообщений

Приложения с интерфейсами, которые используют голос, имеют уникальную привлекательность. Они склонны заставлять своих пользователей чувствовать, что они используют что-то футуристическое. С самого начала Android обладал очень мощной функциональностью преобразования текста в речь (TTS). В этом году Google добавил много высококачественных голосов в свой движок TTS, и это еще одна причина для разработчиков использовать его в своих приложениях.

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

Убедитесь, что у вас установлена ​​Eclipse ADT Bundle. Вы можете скачать его на сайте разработчика Android . Для достижения наилучших результатов вам также потребуется настоящее Android-устройство и несколько друзей, которые могут отправлять вам текстовые сообщения.

Запустите Eclipse и создайте новое приложение для Android. Позвоните в это приложение SMSReader . Если вы думаете, что собираетесь опубликовать это приложение в Google Play, чтобы поделиться им с друзьями, убедитесь, что вы используете уникальное имя пакета. Установите Минимальный необходимый SDK для Android 2.2 и установите Target SDK для Android 4.4 .

Новое окно приложения Android

Это приложение будет иметь одну Activity . Выберите « Создать действие» и выберите « Пустое действие» .

Создать пустую новую активность

Назовите его MainActivity и нажмите « Готово» .

Назовите активность

Это приложение требует трех разрешений:

  • RECEIVE_SMS, чтобы знать, что устройство получило SMS
  • READ_SMS для чтения этого SMS
  • READ_CONTACTS для сопоставления номера телефона отправителя с именем (если возможно)

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

1
2
3
<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 и добавьте в него следующий атрибут:

1
android:screenOrientation=»portrait»

Манифест завершен.

Это не является абсолютно необходимым, но рекомендуется хранить все строки, которые приложение использует в файле res / values ​​/ strings.xml . Отредактируйте этот файл, чтобы он содержал следующее содержимое:

01
02
03
04
05
06
07
08
09
10
11
<?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!
    <string name=»stop_speaking»>Okay!
</resources>

Большинство из этих строк используются на следующем шаге.

Отредактируйте файл res / layout / activity_main.xml, добавив следующее:

  • TextView для отображения имени человека, который отправил последнее SMS
  • TextView для отображения содержимого последних SMS
  • ToggleButton для включения и выключения речевого вывода

После добавления кода для размещения и стилизации этих элементов ваш файл должен иметь следующее содержимое:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<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>

Макет нашего приложения завершен.

Теперь мы собираемся создать вспомогательный класс для движка TTS. Создайте новый класс Java и назовите его Speaker.java . Этот класс используется, чтобы избежать вызова API TTS напрямую из Activity .

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
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. Это важно для создания понятной речи. Добавьте следующий код:

01
02
03
04
05
06
07
08
09
10
11
@Override
public void onInit(int status) {
    if(status == TextToSpeech.SUCCESS){
        // Change this to match your
        // locale
        tts.setLanguage(Locale.US);
        ready = true;
    }else{
        ready = false;
    }
}

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

01
02
03
04
05
06
07
08
09
10
11
12
public void speak(String text){
     
    // Speak only if the TTS is ready
    // and the user has allowed speech
     
    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);
    }
}

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

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

Наконец, добавьте метод для освобождения ресурсов, когда механизм TTS больше не нужен.

1
2
3
4
// Free up resources
public void destroy(){
    tts.shutdown();
}

Отредактируйте MainActivity.java и объявите все виды, которые мы упомянули в макете. Объявите два целых числа, LONG_DURATION и SHORT_DURATION . Это просто значения, которые передаются в метод pause в Speaker .

Также объявите целое число CHECK_CODE . Это значение не важно. Он передается методу startActivityforResult и затем используется для определения результата.

Наконец, объявите объект Speaker объект BroadcastReceiver .

На этом этапе ваш класс должен выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
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. Проверка выполняется с использованием результата другого Activity .

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

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

01
02
03
04
05
06
07
08
09
10
11
12
@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 его метод onReceive . Мы анализируем сообщения, которые поступают в виде байтовых массивов, используя класс SmsMessage . После анализа сообщения мы используем такие методы, как getDisplayMessageBody и getOriginatingAddress чтобы извлечь из него значимую информацию.

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

Добавьте следующий код к реализации:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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(«You have a new message from» + sender + «!»);
                    speaker.pause(SHORT_DURATION);
                    speaker.speak(text);
                    smsSender.setText(«Message from » + sender);
                    smsText.setText(text);
                }
            }
             
        }
    };
}

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

01
02
03
04
05
06
07
08
09
10
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 «unknown number»;
    }
}

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

1
2
3
4
private void registerSMSReceiver() {
    IntentFilter intentFilter = new IntentFilter(«android.provider.Telephony.SMS_RECEIVED»);
    registerReceiver(smsReceiver, intentFilter);
}

Далее мы создаем метод onCreate . Здесь мы инициализируем все объекты, которые мы объявили. Мы инициализируем toggleListener для установки значения allowed в классе Speaker .

После этих инициализаций мы вызываем checkTTS , initializeSMSReceiver и registerSMSReceiver .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@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 мы onDestroy регистрацию нашего получателя и выключили механизм TTS, чтобы освободить ресурсы.

1
2
3
4
5
6
@Override
protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(smsReceiver);
    speaker.destroy();
}

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

Вот образец речи, сгенерированной механизмом TTS:

00:00
00:06

В этом руководстве вы научились не только использовать API преобразования текста в речь, но и использовать широковещательные приемники и понимать исходные данные SMS. Теперь вы можете перейти к дальнейшей настройке этого приложения в соответствии с вашими потребностями.