app/src/main/java/jp/aosystem/readaloud/MainActivity.kt
package jp.aosystem.readaloud
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.graphics.Color
import android.os.Bundle
import android.os.LocaleList
import android.speech.tts.TextToSpeech
import android.speech.tts.UtteranceProgressListener
import android.util.DisplayMetrics
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.SeekBar
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.constraintlayout.widget.ConstraintLayout
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.AdSize
import com.google.android.gms.ads.AdView
import com.google.android.gms.ads.MobileAds
import jp.aosystem.readaloud.databinding.ActivityMainBinding
import java.util.*
class MainActivity : AppCompatActivity(), TextToSpeech.OnInitListener {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: ConstraintLayout
private var inputMethodManager: InputMethodManager? = null
private var themeNumber: Int = 0 //0 or 1
private var localeLanguage: String = ""
private var tabNumber: Int = 1 //タブの番号
private var tts: TextToSpeech? = null
private var speed: Float = 1f
private var pitch: Float = 1f
//adMob
private lateinit var adView: AdView //adMob
private val adSize: AdSize
get() {
val display = windowManager.defaultDisplay
val outMetrics = DisplayMetrics()
display.getMetrics(outMetrics)
val density = outMetrics.density
var adWidthPixels = this.binding.adContainer.width.toFloat()
if (adWidthPixels == 0f) {
adWidthPixels = outMetrics.widthPixels.toFloat()
}
val adWidth = (adWidthPixels / density).toInt()
return AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(this, adWidth)
}
companion object {
private const val AD_UNIT_ID: String = "ca-app-pub-0000000000000000/0000000000" //adMob
private const val RESULT_SETTING_ACTIVITY: Int = 1
internal const val SETTINGS: String = "settings"
internal const val THEME_NUMBER: String = "themeNumber"
internal const val LOCALE_LANGUAGE: String = "localeLanguage"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_main)
this.binding = ActivityMainBinding.inflate(layoutInflater)
this.viewModel = this.binding.root
setContentView(this.viewModel)
//タイトルバー非表示
supportActionBar?.hide()
//テーマ読み込みと設定
this.loadThemeNumber()
this.setTheme()
//adMob
MobileAds.initialize(this) {}
adView = AdView(this)
this.binding.adContainer.addView(adView)
loadBanner()
//
this.tts = TextToSpeech(this, this)
//シークバー
this.seekBarInit()
//背景タッチでテキストエリアからフォーカスを外してキーボードを隠す
this.setTouchListener()
//
this.tabColorClear()
this.binding.textTab1.setTextColor(Color.WHITE)
}
override fun onResume() {
super.onResume()
this.tabNumber = 1
this.loadText()
}
private fun loadBanner() {
adView.adUnitId = AD_UNIT_ID
adView.adSize = adSize
// Create an ad request. Check your logcat output for the hashed device ID to
// get test ads on a physical device, e.g.,
// "Use AdRequest.Builder.addTestDevice("ABCDE0123") to get test ads on this device."
val adRequest = AdRequest
.Builder()
//.addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
.build()
// Start loading the ad in the background.
adView.loadAd(adRequest)
}
override fun onStop() {
super.onStop()
this.saveText(this.binding.editText1.text.toString())
}
//背景タッチでテキストエリアからフォーカスを外してキーボードを隠す
private fun setTouchListener() {
this.inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
this.binding.editText1.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
this.inputMethodManager!!.hideSoftInputFromWindow( //キーボードを隠す
this.binding.editText1.windowToken,
InputMethodManager.HIDE_NOT_ALWAYS
)
}
}
}
private fun tabColorClear() {
this.binding.textTab1.setTextColor(Color.BLACK)
this.binding.textTab2.setTextColor(Color.BLACK)
this.binding.textTab3.setTextColor(Color.BLACK)
this.binding.textTab4.setTextColor(Color.BLACK)
this.binding.textTab5.setTextColor(Color.BLACK)
this.binding.textTab6.setTextColor(Color.BLACK)
this.binding.textTab7.setTextColor(Color.BLACK)
this.binding.textTab8.setTextColor(Color.BLACK)
this.binding.textTab9.setTextColor(Color.BLACK)
}
fun onClickTab1(v: View) {
this.saveText(this.binding.editText1.text.toString())
this.tabNumber = 1
this.loadText()
this.tabColorClear()
this.binding.textTab1.setTextColor(Color.WHITE)
}
fun onClickTab2(v: View) {
this.saveText(this.binding.editText1.text.toString())
this.tabNumber = 2
this.loadText()
this.tabColorClear()
this.binding.textTab2.setTextColor(Color.WHITE)
}
fun onClickTab3(v: View) {
this.saveText(this.binding.editText1.text.toString())
this.tabNumber = 3
this.loadText()
this.tabColorClear()
this.binding.textTab3.setTextColor(Color.WHITE)
}
fun onClickTab4(v: View) {
this.saveText(this.binding.editText1.text.toString())
this.tabNumber = 4
this.loadText()
this.tabColorClear()
this.binding.textTab4.setTextColor(Color.WHITE)
}
fun onClickTab5(v: View) {
this.saveText(this.binding.editText1.text.toString())
this.tabNumber = 5
this.loadText()
this.tabColorClear()
this.binding.textTab5.setTextColor(Color.WHITE)
}
fun onClickTab6(v: View) {
this.saveText(this.binding.editText1.text.toString())
this.tabNumber = 6
this.loadText()
this.tabColorClear()
this.binding.textTab6.setTextColor(Color.WHITE)
}
fun onClickTab7(v: View) {
this.saveText(this.binding.editText1.text.toString())
this.tabNumber = 7
this.loadText()
this.tabColorClear()
this.binding.textTab7.setTextColor(Color.WHITE)
}
fun onClickTab8(v: View) {
this.saveText(this.binding.editText1.text.toString())
this.tabNumber = 8
this.loadText()
this.tabColorClear()
this.binding.textTab8.setTextColor(Color.WHITE)
}
fun onClickTab9(v: View) {
this.saveText(this.binding.editText1.text.toString())
this.tabNumber = 9
this.loadText()
this.tabColorClear()
this.binding.textTab9.setTextColor(Color.WHITE)
}
//テキストを保存
private fun saveText(str: String) {
getSharedPreferences("settings", Context.MODE_PRIVATE).edit().apply {
when (this@MainActivity.tabNumber) {
1 -> putString("editText1", str)
2 -> putString("editText2", str)
3 -> putString("editText3", str)
4 -> putString("editText4", str)
5 -> putString("editText5", str)
6 -> putString("editText6", str)
7 -> putString("editText7", str)
8 -> putString("editText8", str)
9 -> putString("editText9", str)
}
apply()
}
}
//テキストを読み出し
private fun loadText() {
val pref = getSharedPreferences("settings", Context.MODE_PRIVATE)
var editText: String = ""
when (this.tabNumber) {
1 -> editText = pref.getString("editText1", "").toString()
2 -> editText = pref.getString("editText2", "").toString()
3 -> editText = pref.getString("editText3", "").toString()
4 -> editText = pref.getString("editText4", "").toString()
5 -> editText = pref.getString("editText5", "").toString()
6 -> editText = pref.getString("editText6", "").toString()
7 -> editText = pref.getString("editText7", "").toString()
8 -> editText = pref.getString("editText8", "").toString()
9 -> editText = pref.getString("editText9", "").toString()
}
this.binding.editText1.setText(editText, TextView.BufferType.NORMAL)
}
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
//Toast.makeText(applicationContext, "初期化成功", Toast.LENGTH_SHORT).show()
if (this.tts?.isLanguageAvailable(Locale.JAPANESE)!! > TextToSpeech.LANG_AVAILABLE) {
this.binding.textPlayJp.isEnabled = false
}
if (this.tts?.isLanguageAvailable(Locale.ENGLISH)!! > TextToSpeech.LANG_AVAILABLE) {
this.binding.textPlayEn.isEnabled = false
}
val listener = object : UtteranceProgressListener(){
override fun onDone(utteranceId: String?) {
val text: String = resources.getString(R.string.ttsDone)
Toast.makeText(applicationContext, text, Toast.LENGTH_SHORT).show()
}
override fun onError(utteranceId: String?) {
val text: String = resources.getString(R.string.ttsError)
Toast.makeText(applicationContext, text, Toast.LENGTH_SHORT).show()
}
override fun onError(utteranceId: String?, errorCode: Int) {
val text: String = resources.getString(R.string.ttsError)
Toast.makeText(applicationContext, text, Toast.LENGTH_SHORT).show()
}
override fun onStart(utteranceId: String?) {
val text: String = resources.getString(R.string.ttsStart)
Toast.makeText(applicationContext, text, Toast.LENGTH_SHORT).show()
}
override fun onStop(utteranceId: String?, interrupted: Boolean) {
val text: String = resources.getString(R.string.ttsStop)
Toast.makeText(applicationContext, text, Toast.LENGTH_SHORT).show()
}
override fun onBeginSynthesis(utteranceId: String?,sampleRateInHz: Int,audioFormat: Int, channelCount: Int) {
val text: String = resources.getString(R.string.ttsBeginSynthesis)
Toast.makeText(applicationContext, text, Toast.LENGTH_SHORT).show()
}
override fun onAudioAvailable(utteranceId: String?, audio: ByteArray?) {
val text: String = resources.getString(R.string.ttsAudioAvailable)
Toast.makeText(applicationContext, text, Toast.LENGTH_SHORT).show()
}
}
this.tts?.setOnUtteranceProgressListener(listener) //イベントリスナを登録
} else {
val text: String = resources.getString(R.string.ttsInitError)
Toast.makeText(applicationContext, text, Toast.LENGTH_SHORT).show()
}
}
override fun onDestroy() {
this.saveText(this.binding.editText1.text.toString())
this.tts?.shutdown()
super.onDestroy()
}
private fun seekBarInit() {
this.binding.seekBarSpeed.progress = 10
this.binding.seekBarSpeed.min = 2
this.binding.seekBarSpeed.max = 30
this.binding.seekBarPitch.progress = 10
this.binding.seekBarPitch.min = 2
this.binding.seekBarPitch.max = 30
this.binding.seekBarSpeed.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(
seekBar: SeekBar,
progress: Int,
fromUser: Boolean
) { //ツマミがドラッグされると呼ばれる
this@MainActivity.speed = progress.toFloat() / 10f
val str = String.format(Locale.US, "%d%%", (this@MainActivity.speed * 100).toInt())
this@MainActivity.binding.textSpeed.text = str
this@MainActivity.tts?.setSpeechRate(this@MainActivity.speed)
}
override fun onStartTrackingTouch(seekBar: SeekBar) { //ツマミがタッチされた時に呼ばれる
}
override fun onStopTrackingTouch(seekBar: SeekBar) { //ツマミがリリースされた時に呼ばれる
}
})
this.binding.seekBarPitch.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(
seekBar: SeekBar,
progress: Int,
fromUser: Boolean
) { //ツマミがドラッグされると呼ばれる
this@MainActivity.pitch = progress.toFloat() / 10f
val str = String.format(Locale.US, "%d%%", (this@MainActivity.pitch * 100).toInt())
this@MainActivity.binding.textPitch.text = str
this@MainActivity.tts?.setPitch(this@MainActivity.pitch)
}
override fun onStartTrackingTouch(seekBar: SeekBar) { //ツマミがタッチされた時に呼ばれる
}
override fun onStopTrackingTouch(seekBar: SeekBar) { //ツマミがリリースされた時に呼ばれる
}
})
}
fun onClickPlayJp(v: View) {
this.tts?.language = Locale.JAPANESE
this.speechText()
}
fun onClickPlayEn(v: View) {
this.tts?.language = Locale.ENGLISH
this.speechText()
}
fun onClickStop(v: View) {
this.tts?.shutdown()
this.tts = TextToSpeech(this, this)
}
private fun speechText() {
this.tts?.setSpeechRate(this.speed)
this.tts?.setPitch(this.pitch)
val text = this.binding.editText1.text.toString()
this.tts?.speak(text, TextToSpeech.QUEUE_FLUSH, null, "ID")
}
//--------------------------------------------------------------------------------
fun onClickSetting(v: View) {
val intent = Intent(applicationContext, SettingActivity::class.java)
intent.putExtra(THEME_NUMBER, this.themeNumber)
intent.putExtra(LOCALE_LANGUAGE, this.localeLanguage)
startActivityForResult(intent, RESULT_SETTING_ACTIVITY)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
super.onActivityResult(requestCode, resultCode, intent)
if (resultCode == Activity.RESULT_OK && requestCode == RESULT_SETTING_ACTIVITY && intent != null) {
var reCreateFlag: Boolean = false
//
val lastThemeNumber: Int = this.themeNumber
this.themeNumber = intent.getIntExtra(THEME_NUMBER, 0)
if (this.themeNumber != lastThemeNumber) {
this.saveThemeNumber()
reCreateFlag = true
}
//
val lastLocaleLanguage = this.localeLanguage
this.localeLanguage = intent.getStringExtra(LOCALE_LANGUAGE) ?: ""
if (this.localeLanguage != lastLocaleLanguage) {
this.saveLocaleLanguage()
reCreateFlag = true
}
//
if (reCreateFlag) {
recreate()
}
}
}
//----------------------------------------------
//テーマを保存
private fun saveThemeNumber() {
getSharedPreferences(SETTINGS, Context.MODE_PRIVATE).edit().apply {
putInt(THEME_NUMBER, this@MainActivity.themeNumber)
apply()
}
}
//テーマを読み出し
private fun loadThemeNumber() {
val pref = getSharedPreferences(SETTINGS, Context.MODE_PRIVATE)
this.themeNumber = pref.getInt(THEME_NUMBER, 0)
}
//テーマを設定
private fun setTheme() {
when (this.themeNumber) {
0 -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
1 -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
}
}
//----------------------------------------------
//localeLanguageを保存
private fun saveLocaleLanguage() {
getSharedPreferences(SETTINGS, Context.MODE_PRIVATE).edit().apply {
putString(LOCALE_LANGUAGE, this@MainActivity.localeLanguage)
apply()
}
}
//言語設定
override fun attachBaseContext(base: Context) {
val pref = base.getSharedPreferences(SETTINGS, Context.MODE_PRIVATE)
this.localeLanguage = pref.getString(LOCALE_LANGUAGE, "") ?: ""
val loc: Locale? = if (this.localeLanguage != "") Locale(this.localeLanguage) else null
if (loc != null) {
val res = base.resources
val config = Configuration(res.configuration)
val localeList = LocaleList(loc)
LocaleList.setDefault(localeList)
config.setLocales(localeList)
super.attachBaseContext(base.createConfigurationContext(config))
} else {
super.attachBaseContext(base)
}
}
//----------------------------------------------
}
app/src/main/java/jp/aosystem/readaloud/SettingActivity.kt
package jp.aosystem.readaloud
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle
import android.os.LocaleList
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import jp.aosystem.readaloud.databinding.ActivitySettingBinding
import java.util.*
class SettingActivity : AppCompatActivity() {
private lateinit var binding: ActivitySettingBinding
private lateinit var viewModel: ConstraintLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_main)
this.binding = ActivitySettingBinding.inflate(layoutInflater)
this.viewModel = this.binding.root
setContentView(this.viewModel)
//データ受け取り
val intent = this.intent
val themeNumber: Int = intent.getIntExtra(MainActivity.THEME_NUMBER,0)
this.binding.switchTheme.isChecked = themeNumber != 0
val localeLanguage: String = intent.getStringExtra(MainActivity.LOCALE_LANGUAGE) ?: ""
when (localeLanguage) {
"en" -> this.binding.radioLanguageEn.isChecked = true
"ja" -> this.binding.radioLanguageJa.isChecked = true
else -> this.binding.radioLanguageSystem.isChecked = true
}
//タイトルバー非表示
supportActionBar?.hide()
}
fun onClickApply(v: View) {
val themeNumber = if (this.binding.switchTheme.isChecked) 1 else 0
var localeLanguage: String = ""
if (this.binding.radioLanguageEn.isChecked) {
localeLanguage = "en"
} else if (this.binding.radioLanguageJa.isChecked) {
localeLanguage = "ja"
}
val intent = Intent()
intent.putExtra(MainActivity.THEME_NUMBER, themeNumber)
intent.putExtra(MainActivity.LOCALE_LANGUAGE, localeLanguage)
setResult(Activity.RESULT_OK, intent)
finish()
}
fun onClickCancel(v: View) {
val intent = Intent()
setResult(Activity.RESULT_CANCELED, intent)
finish()
}
//--------------------------------------------------
//言語設定
override fun attachBaseContext(base: Context) {
val pref = base.getSharedPreferences(MainActivity.SETTINGS, Context.MODE_PRIVATE)
val localeLanguage: String = pref.getString(MainActivity.LOCALE_LANGUAGE, "") ?: ""
val loc: Locale? = if (localeLanguage != "") Locale(localeLanguage) else null
if (loc != null) {
val res = base.resources
val config = Configuration(res.configuration)
val localeList = LocaleList(loc)
LocaleList.setDefault(localeList)
config.setLocales(localeList)
super.attachBaseContext(base.createConfigurationContext(config))
} else {
super.attachBaseContext(base)
}
}
//--------------------------------------------------
}