テキスト読み上げアプリ
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など)や半角空白記号( )などを変数の名前にできるそうです。
可能であれば、半角バッククォート記号(`)で囲まれた変数の名前は、半角バッククォート記号(`)で囲まずに済む名前に変更したほうが良いのでは、と個人的に思っております。
2025年2月6日に脆弱性CVE-2022-24329へ対応するため、build.gradleの依存ライブラリーを安全な新しいバージョンに更新しました。
/home/◯◯◯/AndroidStudioProjects/Tts/app/build.gradle
――――――――――――――――――――
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'eliphas1810.tts'
compileSdk 34
defaultConfig {
applicationId "eliphas1810.tts"
minSdk 24
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
――――――――――――――――――――
※build.gradleを変更したら必ずAndroid Studioで「Sync Now」をクリックして押してください。
※「Sync Now」をクリックして押して初めてAndroid Studioはbuild.gradleの変更を読み込んで必要なライブラリのjarファイルをダウンロードしてくれます。
・次のアンドロイドの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のプロジェクトの名前です。
画面に表示する文字の日本語対応の設定のXMLファイルを置くres/values-ja/ディレクトリを用意します。
/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のプログラムのパッケージの名前です。