您好,欢迎来到源码搜藏网!分享精神,快乐你我!
[加入VIP] 设为首页 | 收藏本站 | 网站地图 | Sitemap | TAG标签
  • 首 页
  • 在线工具
  • jquery手册
  • 当前位置:首页 > 安卓源码 > 技术博客 >

    使用Android 编写一个简单的大声朗读您的短信应用程序

    时间:2018-08-08 23:47 来源:互联网 作者:源码搜藏 浏览:收藏 挑错 推荐 打印

    使用Android TextToSpeech API,我们可以编写一个简单的应用程序,大声朗读您的短信(短信)信息。 源代码下载: 链接: https://pan.baidu.com/s/1dLLjExMHULa6Mh78DZBIXA 密码: gi2a 我编写了我在本文中解释的应用程序,因为它是我自己想要的。 我经常在离 使用Android TextToSpeech API,我们可以编写一个简单的应用程序,大声朗读您的短信(短信)信息。
    源代码下载:链接: https://pan.baidu.com/s/1dLLjExMHULa6Mh78DZBIXA 密码: gi2a
    我编写了我在本文中解释的应用程序,因为它是我自己想要的。我经常在离开工作之前给我的妻子发短信。一般来说,我没有注意到她的回复,因为我已经开车了,而且在开车时不看我的手机我很自律。这经常导致我错过了她的消息,要求我在回家的路上在商店里挑选一些东西。

    现在我只需启动我的Android应用程序,我的手机就会使用  TextToSpeech API在我开车时向她读取她(或任何传入的短信)消息。  

    更新 - 修复所有版本的权限运行(KitKat通过Nugat)

    我修复了Marshmallow和Nugat上SMS Text的权限问题,现在这段代码将在所有版本上运行。获取上面的v2并在支持TextToSpeech的任何版本上运行。有关添加的代码的详细信息,请参阅下面的修复权限部分

    警告-请注意 MarshmallowNugat 用户-在这一点上我很兴奋,所以我用的版本,释放它释放本文将在Android 4.3的工作-棒棒堂(5.1.1) 在Lollipop之后,Android为SMS提供了更多安全性,因此我必须添加代码以获取SMS消息,该消息在第一次运行时警告用户。该代码暂时不适用于这些平台。我会尽快更新。现在,我希望你会阅读,也许到你完成时我会把它修好(下一天或两天 - 目标为2017年4月27日)。 

    第一版很难看(基本用户界面)

    第一个版本并不漂亮,因为我只是想要功能,并且对创建漂亮的UI不感兴趣。这是一个基本的空Android表单(Activity),我添加了一些基本控件。下图显示了EditText和一个按钮,您可以使用该按钮键入所需的任何文本,并让应用程序为您说出文本。  

    我称应用程序Vext(语音文本portmanteau ^)。

    使用Android 编写一个简单的大声朗读您的短信应用程序

    在派对上的乐趣

    您可以在EditText控件中键入所需的任何文本,按[Speak]按钮,应用程序将以默认设备语音为您说出。你将成为你参加的每一个派对的热门话题。:)

    观看该应用在这个非常短的视频中讲一个简短的短语。

    https://youtu.be/D5X3z7miXjQ  (在新标签页中打开)

    在活动的更下方的SeekBar控件(在Windows世界中也称为Slider)允许您设置语音的音量。应用程序首次启动时,通过在MainActivity上调用以下方法获取最大卷:

    int maxVol = am.getStreamMaxVolume(am.STREAM_MUSIC);

    第一次运行时,它将默认为3.设置值后,我将值存储在SharedPreferences中,以便应用程序的每次初始运行都会记住您希望语音的音量。

    我们将在本文的正文中详细介绍这些细节。这是我们将在本文中介绍的概念。

    我们将涵盖的概念

    • 接收短信,抓取信息正文
    • 使用TextToSpeech API
    • 在SharedPreferences中设置和保存TextToSpeech卷

    调用功能

    目前,运行该应用程序将是打开和关闭该功能的方法。这意味着,启动应用程序,将向您读取以下传入消息。停止应用程序,您的文本将不再被大声朗读。

    Android Widget

    这个应用程序很适合作为Android Widget(可从您的家和锁屏获得),我已经在努力了。但是,用于小部件的Android API很脆弱且难以使用。事情肯定会更容易。正如我试图实现它,我遇到了无法解释的错误。直到我得到那些修复,我们将不得不满足于这个基本的应用程序。

    这是一个很长的介绍所以让我们跳进一些代码。我们首先来看看一切开始的MainActivity代码。

    MainActivity onCreate()

    当Android应用程序启动时,将加载MainActivity并onCreate()调用函数。该方法使我们有机会在应用程序启动时进行一些初始化。我不会向您展示UI组件的所有初始化,因此我可以专注于本文的主题。我们在onCreate()中看到的第一个对我们感兴趣的代码是:

    AudioManager am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
    int maxVol = am.getStreamMaxVolume(am.STREAM_MUSIC);
    Log.d("MainActivity", "max vol : " + String.valueOf(maxVol));
    volumeSeekBar.setMax(maxVol);

    设置UI组件(SeekBar)

    此代码允许我们使用。获取系统音频服务的最大音量级别AudioManager要设置TextToSpeech语音的音量,我们将使用setStreamVolume()稍后命名的AudioManager方法这部分代码获得了可能的最大音量,因此我们可以设置我们的SeekBar(思考滑块)最大值。通常这个值大约是13到15.这确保了用户只能使用SeekBar将音量设置为有效值。

    设置当前音量

    下一行调用我编写的设置当前音量值的方法:

    setCurrentVolumeLevel();

    该方法在MainActivity.java文件中稍微向下,它看起来像:

    private void setCurrentVolumeLevel() {
     SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
     int volumeLevel = 0;
     volumeLevel = preferences.getInt("volumeLevel", 3);
     volumeSeekBar.setProgress(volumeLevel);
    }

    使用SharedPreferences非常简单且有帮助

    此方法向您展示如何从Android读取SharedPreferences这些值只能由此应用程序读取和写入,因此它们被认为是安全的。当然,我们只是保存并检索当前卷的值,因此无关紧要。使用SharedPreferences的价值在于,每次运行应用程序时都会设置和记住我们的值。

    一旦我们获得了我们从中获取的preferences 对象,我们就PreferenceManager可以查询它可能已经保存的值。在我们的例子中,我们正在寻找一个名为的整数值volumeLevel我们使用的getInt()方法是获取我们要查找的值的名称,如果找不到则返回默认值(在我们的例子中为3)。应用程序第一次运行时,将找不到该值,因此我们将该值设置为3。

    更新SeekBar UI

    获得值(或默认值)后,我们只需setProgress()使用值调用方法,以确保使用正确的值更新SeekBar UI。该方法返回,我们又回来了onCreate()

    意图和广播

    在Android世界中,只要您想要解决一些通用功能,您就可以使用Intent来实现。例如,如果您希望用户能够通过您的应用观看视频,则可能已有可用的视频服务。这意味着您只需要正确设置一个Intent并请求服务处理该操作。  

    对于Vext,我认为使TextToSpeech 功能可用的最简单方法是创建一个BroadcastReceiver,我们可以在需要时通过Intent调用它。这也将允许在我实现Android Widget时更容易访问该功能(稍后将详细介绍)。

    正如我所说,你可以在你的应用程序之外请求功能,但在我们的情况下,我想保持服务本地,我已经知道我可以通过创建一个下降的类来做到这一点BroadcastReceiver稍后,我们将看到这个添加的类名为  TTSReceiver

    在MainActivity onCreate()中,您可以看到我在何处初始化lbm 为此用途命名的成员变量,然后创建一个新变量TTSReceiver

    lbm = LocalBroadcastManager.getInstance(this);
    ttsReceiver = new TTSReceiver();

    这将准备TextToSpeech系统,因为我们将使用它。但是要了解如何在我们的应用程序中实现TextToSpeech,让我们仔细看看TTSReceiver类。

    public class TTSReceiver extends BroadcastReceiver {
        private TextToSpeech tts;
        private Set<Voice> allVoices;
        private String messageBodyText;
        private TextToSpeech.OnInitListener ttsListener;
    
        @Override
        public void onReceive(Context context, Intent intent) {
    
            Log.d("MainActivity", "onReceive() fired!");
            SharedPreferences preferences = 
                  PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
            int volumeLevel = 0;
            volumeLevel = preferences.getInt("volumeLevel",3);
            Log.d("MainActivity", "volumeLevel : " + String.valueOf(volumeLevel));
            AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
            am.setStreamVolume(am.STREAM_MUSIC, volumeLevel, 0);
    
            messageBodyText =  intent.getStringExtra("MESSAGE_BODY");
    
            if (ttsListener == null) {
                ttsListener = new TextToSpeech.OnInitListener() {
                    @Override
                    public void onInit(int status) {
                        // tts.speak(messageBodyText, TextToSpeech.QUEUE_ADD, null, "1st1");
                    }
                };
            }
    
            if (tts == null) {
                tts = new TextToSpeech(context, ttsListener);
            }
    
            tts.speak(messageBodyText, TextToSpeech.QUEUE_ADD, null, "2nd1");
        }
    }

    这个类非常简单,我们将回到MainActivity代码,稍后通过Intent调用它,这样你就可以看到它是如何被调用的。但是,首先让我们来看看我们在这里有什么。

    BroadcastReceiver onRecieve()

    BroadcastReceiver 调用它时,很容易采取一些操作您只需覆盖该onReceive()方法,操作系统会在事件发生时通知您。这是当你新建Intent并通过它调用BroadcastReceiver时将运行的代码LocalBroadcastManager您可以查看这些基础知识的更多详细信息,但是让我添加此类必须注册的位置,以便您了解系统如何知道如何查找此类。

    AndroidManifest.xml中

    每当您添加BroadcastReceiver时,您还必须向系统注册该类,以便它知道如何查找该类。您可以通过AndroidManifest 项目中找到来实现。虽然我不喜欢在读者身上放一堆代码,但我会在这里展示整个AndroidManifest,因为我们还需要允许一些Android权限,你现在也可以看到它们。我还将粗体显示TTSReceiver定义的部分,以便您可以看到它。  

    大多数是在AndroidManifest 创建项目时由Android Studio项目模板生成的。

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    
        package="us.raddev.vext">
        <uses-permission android:name="android.permission.RECEIVE_SMS" />
        <uses-permission android:name="android.permission.READ_CONTACTS"/>
        <application
    
            android:allowBackup="true"
    
            android:icon="@mipmap/ic_launcher"
    
            android:label="@string/app_name"
    
            android:supportsRtl="true"
    
            android:theme="@style/AppTheme">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <receiver android:name=".MsgReceiver">
                <intent-filter>
                    <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
                </intent-filter>
            </receiver>
            <receiver android:name=".TTSReceiver">
                <intent-filter>
                    <action android:name="us.raddev.vext.message" />
                </intent-filter>
            </receiver>
        </application>
    </manifest>

    Android权限

    收到短信

    如您所见,前两个粗体线是我添加的权限。第一个允许我们的应用程序接收短信。这意味着用户将在安装应用程序时收到警告。如果用户想要她可以取消安装。  

    阅读联系信息

    此应用还需要阅读联系信息以获取向您发送消息的用户的名称。  

    仔细观察,您会发现我们的MainActivity也在清单(.MainActivity)中注册。

    TTSReceiver注册一个动作

    您还可以看到TTSReceiver注册了一个名为“us.raddev.text.message”的动作。这是我们将添加到Intent的动作,这样当我们广播动作时就会触发。

    当动作触发时,该onReceive() 方法将运行,我们可以运行我们的代码。

    当onReceive()方法事件发生时,我们执行以下操作:

    从首选项中读取音量级别

    SharedPreferences preferences =
      PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); int volumeLevel = 0;
     volumeLevel = preferences.getInt("volumeLevel",3);

    我们将音量级别存储在变量中,以便我们可以使用它来设置TextToSpeech 引擎将使用的音量级别

    设置AudioManager音量级别

    AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); 
    am.setStreamVolume(am.STREAM_MUSIC, volumeLevel, 0);

    读取字符串传递:MESSAGE_BODY

    messageBodyText =  intent.getStringExtra("MESSAGE_BODY");

    当我们创建我们的Intent(回到MainActivity onCreate() - 稍后将显示此代码)时,我们在Intent上设置一个字符串,我们将使用名称“MESSAGE_BODY”来引用它。在这里,我们获取该文本并将其保存到名为messageBodyText的成员变量中。  

    这是什么文字?

    这通常是文本消息的主体。但是,由于我们使用此BroadcastReceiver来对TextToSpeech进行任何调用,因此这是我们想要大声说出的任何文本。

    最后,我们准备初始化TextToSpeech,以便它为我们说出我们的文本。

    初始化TextToSpeech

    if (ttsListener == null) {
        ttsListener = new TextToSpeech.OnInitListener() {
            @Override
            public void onInit(int status) {
                // tts.speak(messageBodyText, TextToSpeech.QUEUE_ADD, null, "1st1");
            }
        };
    }
    
    if (tts == null) {
        tts = new TextToSpeech(context, ttsListener);
    }

    您可以看到我们必须初始化,TTSListener 以便我们可以使用它来初始化TextToSpeech 它实际上会说文本对象。我将这些对象存储在成员变量中,以便在应用程序的生命周期内初始化TTS。但是,我确实遇到了一些陌生感。

    奇怪的事情,重复或不说话

    尝试初始化TextToSpeech时遇到了一些问题。我发现如果我在上面显示的onInit()方法中进行了第一次调用(现在已经注释,因为它不应该使用),TextToSpeech会重复短语。这很奇怪,代表了一个挑战,因为当我没有这样做时,TextToSpeech就不会说我的任何文字了。  

    解决方法和它的工作原理

    我最后通过第一次调用初始化TTS来解决这个问题,该调用要求它说一个空字符串。是的,这似乎很荒谬,但我了解到其他人也有奇怪的重复。阅读此StackOverflow以获取更多信息: 

    连续调用TextToSpeech.OnInitListener.onInit(int)^

    一旦所有内容都被初始化,我们就可以调用speak()方法并让它说出我们的文本。

    TextToSpeech Speak()方法

    tts.speak(messageBodyText, TextToSpeech.QUEUE_ADD, null, "2nd1");

    一旦掌握了所有这些,就可以轻松完成。当然,你必须设置BroadcastReceiver,否则这将无法正常工作。speak()方法需要三个参数,我们应该更仔细地了解它们:

    1. messageBodyText:TTS将发言的文本
    2. TextToSpeech.QUEUE_ADD:如何处理多个发言请求 - 在我们的例子中,我们告诉它将每个请求添加到队列并在可能的情况下说出来。你也可以使用QUEUE_FLUSH,它会刷新(取消)其他人,只说最新的。
    3. 字符串:在我们的例子中,我添加了“2nd1”,这是毫无意义的。这只是一个用于标识消息的字符串。

    现在我们已经看到了TTSReceiver(BroadcastReceiver)如何工作,现在我们可以更好地了解如何调用它。我们用方法用空字符串(如前所述)进行第一次调用MainActivity.onCreate()

    以下是我们如何创建Intent并调用TTSReceiver来说出一些文本:

    Intent intent = new Intent("us.raddev.vext.message");
    intent.putExtra(MESSAGE_BODY,"" );
    lbm.sendBroadcast(intent);

    当我们创建Intent时,我们提供了我们想要采取的动作的名称。请记住,“.message”是任意选择的。它可能是“us.raddev.vext.elephant”如果我想要的话。:)

    接下来,我们将Extra(字符串)添加到Intent并将其命名为“MESSAGE_BODY”。在这种情况下,我们添加的字符串是一个空字符串,所以它不是那么令人兴奋。  

    广播我们的意图

    最后我们调用我们LocalBroadcastManager 的方法并使用我们的方法调用sendBroadcast方法intent当我们这样做时,系统找到匹配的类(TTSReceiver)并onReceive()运行方法。就这么简单。

    您可以看到非常相似的代码附加到说话按钮onclick监听器。这样,当用户键入一些文本并单击MainActivity上的按钮时,她将听到它说出来。

    speakButton.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            String outText = textToSpeak.getText().toString();
            if (outText != null) {
                Intent intent = new Intent("us.raddev.vext.message");
                intent.putExtra(MESSAGE_BODY,outText );
                lbm.sendBroadcast(intent);
                Log.d("MainActivity", "Button click ended!");
            }
        }
    });

    我已经加粗了上面的重要代码。您可以看到,这里的最大区别是我们从textToSpeak EditText控件获取文本,并且当我们在Intent上调用putExtra时使用它。

    这是我们在TTSReceiver.onReceive()方法中检索的字符串,因此该TextToSpeech.speak()方法知道该说什么。

    一切如何工作现在更有意义

    现在我们已经了解了所有这些,了解SMS文本消息到达时我们将会做什么将会容易得多。现在唯一的区别是我们的应用程序将说出SMS文本消息中的单词。它是如何做到的?一样的方法。唯一的另一件事是我们获取SMS的正文文本并将其传递给我们的TTSReceiver,以便它为我们说话。

    请更多BroadcastReceiver

    在Android上接收短信并获取正文非常容易。现在你更容易理解a,BroadcastReceiver 因为接收SMS也是通过BroadcastReceiver完成的。你甚至可以谷歌如何做到这一点。回顾上面的AndroidManifest并查找文本.MsgReceiver,您将看到我们在MsgReceiver 收到传入短信时注册了一个名为运行的类

    这是我们MsgReceiver 班级的样子。同样,一切都在onReceive()方法中发生

    public void onReceive(Context context, Intent intent) {
        if (lbm == null && ttsReceiver == null) {
            lbm = LocalBroadcastManager.getInstance(context);
            ttsReceiver = new TTSReceiver();
            lbm.registerReceiver(ttsReceiver,new IntentFilter("us.raddev.vext.message"));
        }
    
        SmsMessage message = null;
        String from = null;
    
        message = GetMessage(intent);
        from = message.getOriginatingAddress();
    
        if (message == null){
            message = GetMessage(intent);
        }
    
        from = message.getOriginatingAddress();
        String body = message.getMessageBody();
        Toast.makeText(context,body,  Toast.LENGTH_SHORT).show();
        long receivedDate = message.getTimestampMillis();
        Date date = new Date(receivedDate);
        DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy - HH:mm:ss");
        String formattedDate = formatter.format(date);
    
        ///Resolving the contact name from the contacts.
        Uri lookupUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, 
               Uri.encode(from));
        Cursor c = context.getContentResolver().query(lookupUri, new String[]
                {ContactsContract.Data.DISPLAY_NAME},null,null,null);
        try {
            Intent ttsIntent = new Intent("us.raddev.vext.message");
            boolean isSuccess = c.moveToFirst();
            if (isSuccess) {
                // got a contact name
                String displayName = c.getString(0);
                String ContactName = displayName;
                Log.d("MainActivity", "ContactName : " + ContactName);
                ttsIntent.putExtra(MESSAGE_BODY,"Incoming from " + ContactName );
            }
            else{
                //doesn't have name in contacts
                ttsIntent.putExtra(MESSAGE_BODY,"Incoming message");
            }
            lbm.sendBroadcast(ttsIntent);
            lbm.unregisterReceiver(ttsReceiver);
        }
        catch (Exception e) {
            // TODO: handle exception
        }finally{
            c.close();
        }
    
        Intent ttsIntent = new Intent("us.raddev.vext.message");
        ttsIntent.putExtra(MESSAGE_BODY,body );
        lbm.sendBroadcast(ttsIntent);
        lbm.unregisterReceiver(ttsReceiver);
    
        Log.d("MainActivity", "from : " + from);
        Log.d("MainActivity", "body : " + body);
        Log.d("MainActivity", "receivedDate : " + formattedDate);
    
    }

    您可以简单地阅读该代码和几条评论,并找出所做的一切。

    但是,让我总结一下它的作用。

    SMS MsgReceiver摘要

    1. 检查成员变量以查看它们是否已经初始化(或者这是否是首次运行)。
    2. 调用名为的本地方法GetMessage()获取实际的SMS消息对象。这种方法简单地包含了一些东西,因为Android SMS已经更改了版本。  
    3. 从SMS消息对象中获取我们想要使用的一些信息(originatingAddresstextBody
    4. 通过originatingAddress from获取联系- 如果存在
    5. 如果存在联系请求TTSReceiver宣布“从<contactname>传入”
    6. 如果联系人不存在,请求TTSReceiver宣布“传入消息”
    7. 要求TTSReceiver 发言文本正文。

    而已。

    这很容易。抓取代码并在Android手机上试用。我想你真的很喜欢它。

    修复权限

    以前我遇到过一个问题,我没有在Marshmallow(6.x)及更高版本上正确地请求权限。我已经研究过如何做到这一点,而且添加的代码不多。    

    App首次运行

    您将此代码添加到您MainActivity 的应用程序中,以便当用户第一次通知应用程序正在请求权限并且必须响应以允许权限时运行应用程序。同样,只有当应用程序在Marshmallow或更新版本上运行时才会出现这种情况如果权限在Lollipop或更低版本,则以常规方式处理权限。

    这是用户的样子。在我们的例子中,我们需要两个权限(RECEIVE_SMS和READ_CONTACTS),因此用户被查询两次,每次访问一次。

    使用Android 编写一个简单的大声朗读您的短信应用程序使用Android 编写一个简单的大声朗读您的短信应用程序

    拒绝任何一个和Vext将无法工作

    如果用户拒绝,那么Vext应用程序将无法运行。这可以通过将联系人的读取移出MsgReceiver类来解决,但我现在不会担心这一点。现在,这是全有或全无。如果用户在不允许权限的情况下运行应用程序并且SMS文本到达,则应用程序将崩溃。没有警告没有设置烫发。在开发Android应用程序时,这可能会有点混乱,因此请记住这一点。

    这是我添加到MainActivity以处理权限的代码。  

    许可方法

    我添加了一个我命名的新方法,requestAllPermissions()并在MainActivity.onCreate()方法的顶部添加了一个调用,以确保在第一次运行应用程序时查询用户。

    这是非常直接的整个方法:

    private void requestAllPermissions() {
        String permission = Manifest.permission.RECEIVE_SMS;
        String permission2 = Manifest.permission.READ_CONTACTS;
        int grant = ContextCompat.checkSelfPermission(this, permission);
        if ( grant != PackageManager.PERMISSION_GRANTED) {
            String[] permission_list = new String[2];
            permission_list[0] = permission;
            permission_list[1] = permission2;
            ActivityCompat.requestPermissions(this, permission_list, 2);
        }
    }

    您所做的就是设置一个字符串权限数组,然后将它们添加到ActivityCompat.requestPermissions()方法并调用该方法。一旦该代码运行并且用户接受该代码,该应用程序就会为这些权限设置。

    就这么简单。

    使用Android 编写一个简单的大声朗读您的短信应用程序转载http://www.codesocang.com/appboke/38812.html
    标签:网站源码