Приложения с интерфейсами, которые используют голос, имеют уникальную привлекательность. Они склонны заставлять своих пользователей чувствовать, что они используют что-то футуристическое. С самого начала Android обладал очень мощной функциональностью преобразования текста в речь (TTS). В этом году Google добавил много высококачественных голосов в свой движок TTS, и это еще одна причина для разработчиков использовать его в своих приложениях.
В этом руководстве вы узнаете, как создать простое приложение с минималистским пользовательским интерфейсом, которое может принимать текстовые сообщения и читать их пользователю.
Предпосылки
Убедитесь, что у вас установлена Eclipse ADT Bundle. Вы можете скачать его на сайте разработчика Android . Для достижения наилучших результатов вам также потребуется настоящее Android-устройство и несколько друзей, которые могут отправлять вам текстовые сообщения.
1. Создайте новый проект
Запустите Eclipse и создайте новое приложение для Android. Позвоните в это приложение SMSReader . Если вы думаете, что собираетесь опубликовать это приложение в Google Play, чтобы поделиться им с друзьями, убедитесь, что вы используете уникальное имя пакета. Установите Минимальный необходимый SDK для Android 2.2 и установите Target SDK для Android 4.4 .
Это приложение будет иметь одну Activity
. Выберите « Создать действие» и выберите « Пустое действие» .
Назовите его MainActivity и нажмите « Готово» .
2. Отредактируйте Манифест
Это приложение требует трех разрешений:
- 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»
|
Манифест завершен.
3. Отредактируйте strings.xml
Это не является абсолютно необходимым, но рекомендуется хранить все строки, которые приложение использует в файле 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>
|
Большинство из этих строк используются на следующем шаге.
4. Редактировать макет
Отредактируйте файл 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>
|
Макет нашего приложения завершен.
5. Создайте вспомогательный класс
Теперь мы собираемся создать вспомогательный класс для движка 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();
}
|
6. Отредактируйте класс активности
Отредактируйте 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();
}
|
7. Запустите и проверьте
Приложение теперь готово к тестированию. Скомпилируйте и запустите его на физическом устройстве Android. Нажмите кнопку-переключатель, чтобы включить голос и отправить себе SMS-сообщение с другого телефона или попросить одного из ваших друзей сделать это. Скоро вы сможете услышать, как ваш телефон зачитывает для вас смс.
Вот образец речи, сгенерированной механизмом TTS:
Вывод
В этом руководстве вы научились не только использовать API преобразования текста в речь, но и использовать широковещательные приемники и понимать исходные данные SMS. Теперь вы можете перейти к дальнейшей настройке этого приложения в соответствии с вашими потребностями.