テキスト読み上げアプリ

 Githubでテキスト読み上げアプリの.apkファイルなどをパブリック ドメインで公開しております。

 マイクロソフトのBing検索エンジンで「github android-tts」などで検索してみてください。

 残念ながらグーグル検索エンジンでは検索できません。



 テキストを読み上げるアプリです。


 AQUOS sense3、Pixel 7aで動作を確認できました。


 AmazonのFireOSのFireHDタブレットでは動作しません。



 ※下記のXMLファイルやKotlinのプログラムなどのコードをコピペする場合は、2文字の全角空白を4文字の半角空白に置換してください。


 また、Android StudioにJavaやKotlinなどのプログラムのコードをコピペして、「import android.R」が自動で追加されてしまったら、削除してください。

 「android.R」は、「R.layout.activity_main」や「R.id.◯◯◯」の「R」とは違います。

 そのため、「import android.R」が有ると、コンパイル エラーが発生してしまいます。


 Android StudioにJavaやKotlinなどのプログラムのコードをコピペすると、変数の名前が半角バッククォート記号(`)で囲まれる事が有ります。

 Kotlinでは変数の名前を半角バッククォート記号(`)で囲むと予約語(inやnullなど)や半角空白記号( )などを変数の名前にできるそうです。

 可能であれば、半角バッククォート記号(`)で囲まれた変数の名前は、半角バッククォート記号(`)で囲まずに済む名前に変更したほうが良いのでは、と個人的に思っております。



 ・次のアンドロイドのTTS(テキスト トゥ スピーチ)の設定をAndroidManifest.xmlに追加します。


アンドロイドのTTS(テキスト トゥ スピーチ)の設定

――――――――――――――――――――

  <queries>

    <intent>

      <action android:name="android.intent.action.TTS_SERVICE" />

    </intent>

  </queries>

――――――――――――――――――――



/home/◯◯◯/AndroidStudioProjects/Tts/app/src/main/AndroidManifest.xml

――――――――――――――――――――

<?xml version="1.0" encoding="utf-8"?>


<manifest

  xmlns:android="http://schemas.android.com/apk/res/android"

  xmlns:tools="http://schemas.android.com/tools"

>


  <application

    android:allowBackup="true"

    android:dataExtractionRules="@xml/data_extraction_rules"

    android:fullBackupContent="@xml/backup_rules"

    android:icon="@mipmap/ic_launcher"

    android:label="@string/app_name"

    android:supportsRtl="true"

    android:theme="@style/Theme.Tts"

    tools:targetApi="31"

  >

    <activity

      android:name=".MainActivity"

      android:exported="true"

    >

      <intent-filter>

        <action android:name="android.intent.action.MAIN" />


        <category android:name="android.intent.category.LAUNCHER" />


      </intent-filter>

    </activity>


  </application>


  <queries>

    <intent>

      <action android:name="android.intent.action.TTS_SERVICE" />

    </intent>

  </queries>


</manifest>

――――――――――――――――――――

 ◯◯◯はLinux Mintのユーザー名です。

 Ttsは著者が付けたAndroid Studioのプロジェクトの名前です。




/home/◯◯◯/AndroidStudioProjects/Tts/app/src/main/res/values/strings.xml

――――――――――――――――――――

<resources>

  <string name="app_name">Tts</string>


  <string name="speak">Speak</string>


  <string name="cancel">Cancel</string>


  <string name="text_to_speech_is_busy">TextToSpeech is busy now.</string>


</resources>

――――――――――――――――――――

 ◯◯◯はLinux Mintのユーザー名です。

 Ttsは著者が付けたAndroid Studioのプロジェクトの名前です。



/home/◯◯◯/AndroidStudioProjects/Tts/app/src/main/res/values-ja/strings.xml

――――――――――――――――――――

<resources>

  <string name="app_name">Tts</string>


  <string name="speak">読み上げ</string>


  <string name="cancel">キャンセル</string>


  <string name="text_to_speech_is_busy">読み上げ機能が混雑しています。</string>


</resources>

――――――――――――――――――――

 ◯◯◯はLinux Mintのユーザー名です。

 Ttsは著者が付けたAndroid Studioのプロジェクトの名前です。



/home/◯◯◯/AndroidStudioProjects/Tts/app/src/main/res/layout/activity_main.xml

――――――――――――――――――――

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  android:orientation="vertical"

  android:layout_width="match_parent"

  android:layout_height="match_parent"

>

  <ScrollView

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:fillViewport="true"

  >

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

      android:orientation="vertical"

      android:layout_width="match_parent"

      android:layout_height="match_parent"

    >


      <Button

        android:id="@+id/speak"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="@string/speak"

        android:layout_gravity="center"

      />


      <Button

        android:id="@+id/cancel"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="@string/cancel"

        android:layout_gravity="center"

      />


      <EditText

        android:id="@+id/editText"

        android:inputType="textMultiLine"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

      >

        <requestFocus/>

      </EditText>


    </LinearLayout>

  </ScrollView>

</LinearLayout>

――――――――――――――――――――

 ◯◯◯はLinux Mintのユーザー名です。

 Ttsは著者が付けたAndroid Studioのプロジェクトの名前です。



/home/◯◯◯/AndroidStudioProjects/Tts/app/src/main/java/eliphas1810/tts/MainActivity.kt

――――――――――――――――――――

package eliphas1810.tts


import android.os.Bundle

import android.speech.tts.TextToSpeech

import android.widget.Button

import android.widget.EditText

import android.widget.Toast

import androidx.activity.OnBackPressedCallback

import androidx.appcompat.app.AppCompatActivity

import java.util.concurrent.Executors

import java.util.concurrent.ScheduledExecutorService

import java.util.concurrent.TimeUnit



class MainActivity : AppCompatActivity(), TextToSpeech.OnInitListener {



  var textToSpeech: TextToSpeech? = null



  var editText: EditText? = null


  var speakButton: Button? = null

  var cancelButton: Button? = null



  var isStopping: Boolean = false

  var isStarting: Boolean = false

  var isCompleted: Boolean = true



  var lineList: List<String> = mutableListOf<String>()

  var lineIndex = 0

  var maxLineIndex = 0



  var scheduledExecutorService: ScheduledExecutorService? = null



  //メモリー上に作成される時にのみ呼ばれます。

  override fun onCreate(savedInstanceState: Bundle?) {

    try {

      super.onCreate(savedInstanceState)

      setContentView(R.layout.activity_main)



      //戻るボタン、戻るジェスチャーを無効化

      onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {

        override fun handleOnBackPressed() {}

      })



      textToSpeech = TextToSpeech(this, this)



      editText = findViewById(R.id.editText)



      speakButton = findViewById(R.id.speak)

      cancelButton = findViewById(R.id.cancel)



      speakButton?.isClickable = true

      cancelButton?.isClickable = false



      speakButton?.setOnClickListener { view ->

        try {


          if(textToSpeech?.isSpeaking ?: true) {

            Toast.makeText(applicationContext, getString(R.string.text_to_speech_is_busy), Toast.LENGTH_LONG).show()

            return@setOnClickListener

          }


          if (isStarting) {

            return@setOnClickListener

          }


          speakButton?.isClickable = false

          cancelButton?.isClickable = true

          isStarting = true

          isStopping = false


          var text = editText?.text.toString() ?: ""


          if (text.length == 0) {

            isStarting = false

            isStopping = false

            speakButton?.isClickable = true

            cancelButton?.isClickable = false

            return@setOnClickListener

          }


          text = text.replace("\r\n", "\n")

          text = text.replace("\r", "\n")

          lineList = text.split("\n")

          lineIndex = 0

          maxLineIndex = lineList.size - 1


          scheduledExecutorService = Executors.newSingleThreadScheduledExecutor()

          scheduledExecutorService?.scheduleAtFixedRate(

            {

              try {

                if (isCompleted) {


                  if (maxLineIndex < lineIndex || isStopping) {

                    isStarting = false

                    isStopping = false

                    speakButton?.isClickable = true

                    cancelButton?.isClickable = false


                    lineIndex = 0


                    scheduledExecutorService?.shutdown()


                    return@scheduleAtFixedRate

                  }


                  var line = lineList[lineIndex]


                  isCompleted = false

                  if (1 <= line.length) {

                    textToSpeech?.speak(line, TextToSpeech.QUEUE_FLUSH, null, "line" + (lineIndex + 1))

                  }

                  lineIndex += 1

                  isCompleted = true

                }

              } catch (exception: Exception) {

                Toast.makeText(applicationContext, exception.toString(), Toast.LENGTH_LONG).show()

                throw exception

              }

            },

            1, //1回目までの時間間隔の時間数

            1, //1回目以降の時間間隔の時間数

            TimeUnit.SECONDS //時間の単位。秒。

          )


        } catch (exception: Exception) {

          Toast.makeText(applicationContext, exception.toString(), Toast.LENGTH_LONG).show()

          throw exception

        }

      }



      cancelButton?.setOnClickListener { view ->

        try {


          if (isStarting == false) {

            return@setOnClickListener

          }


          speakButton?.isClickable = false

          cancelButton?.isClickable = false


          isStopping = true


        } catch (exception: Exception) {

          Toast.makeText(applicationContext, exception.toString(), Toast.LENGTH_LONG).show()

          throw exception

        }

      }




    } catch (exception: Exception) {

      Toast.makeText(applicationContext, exception.toString(), Toast.LENGTH_LONG).show()

      throw exception

    }

  }



  override fun onInit(status: Int) {

    if (status == TextToSpeech.SUCCESS) {


      //textToSpeech?.setSpeechRate(1.0f) //読み上げ速度

    }

  }



  //メモリーから破棄される時にのみ呼ばれます。

  override fun onDestroy() {

    try {



      textToSpeech?.shutdown()

      textToSpeech = null



      scheduledExecutorService?.shutdownNow()

      scheduledExecutorService = null



    } catch (exception: Exception) {

      Toast.makeText(applicationContext, exception.toString(), Toast.LENGTH_LONG).show()

      throw exception

    } finally {

      super.onDestroy()

    }

  }

}

――――――――――――――――――――

 ◯◯◯はLinux Mintのユーザー名です。

 Ttsは著者が付けたAndroid Studioのプロジェクトの名前です。

 eliphas1810/ttsは著者が付けたJavaやKotlinのプログラムのパッケージのディレクトリの相対パスです。

 eliphas1810.ttsは著者が付けたJavaやKotlinのプログラムのパッケージの名前です。

  • Xで共有
  • Facebookで共有
  • はてなブックマークでブックマーク

作者を応援しよう!

ハートをクリックで、簡単に応援の気持ちを伝えられます。(ログインが必要です)

応援したユーザー

応援すると応援コメントも書けます

アンドロイド スマホのアプリを作ろう! エリファス1810 @Eliphas1810

★で称える

この小説が面白かったら★をつけてください。おすすめレビューも書けます。

カクヨムを、もっと楽しもう

この小説のおすすめレビューを見る

この小説のタグ