はじめに
こんにちは。
前回の投稿ではTextToSpeechを使い、とりあえず喋らせるところまででした。
今回はTextToSpeechをforeground service化し、バックグラウンドで再生できるようにしてみます。
(レイアウトXMLやTextToSpeechのイベントリスナーは前回と同じ内容のため、省略します)
前回の記事 : [Android] KotlinでTextToSpeech
環境
- macOS High Sierra 10.13.4
- Android Studio 3.0.1
- Nexus6 (Android8.1.0, API27 エミュレータ)
MainActivity.kt
前回はMainActivityの中でTextToSpeechを初期化・実行していましたが、今回はforeground serviceで実行しますので、MainActivityではサービスを開始するだけです。
なお、onClick内でSDKのバージョン判定を行い、OREO以降でのみ動作するコードになっています。
(もちろんNougat以下でもサービス実行できるコードを併記すべきですが、まだよく分かっておらず勉強中です…申し訳ありません)
1 2 3 4 5 6 7 8 9 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 | class MainActivity : AppCompatActivity(), View.OnClickListener{ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener) speech_ja.setOnClickListener(this) speech_en.setOnClickListener(this) } override fun onClick(v: View) { val input : String val langLocale : Locale when (v.id) { R.id.speech_ja -> { input = input_ja.text.toString() langLocale = Locale.JAPANESE } R.id.speech_en -> { input = input_en.text.toString() langLocale = Locale.ENGLISH } else -> { input = input_en.text.toString() langLocale = Locale.ENGLISH } } if (langLocale != null && input.isNotBlank()) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { // サービス内で実行されるTextToSpeechに渡す値を設定 val serviceIntent = Intent(this, ForegroundService::class.java) serviceIntent.putExtra("langLocale", langLocale) serviceIntent.putExtra("input", input) // フォアグラウンドサービス開始 startForegroundService(serviceIntent) } } } } |
ForegroundService.kt
サービス生成時に呼ばれる
onCreate
の中でTextToSpeechを初期化します。
サービス開始時には
onStartCommand
が呼ばれるので、その中でTextToSpeechを実行します。
Activity側で
bindService
でサービスを開始した場合には、
onBind
が呼ばれます(今回は使用しません)。
また、foreground service実行に必要となるNotificationを作成します。
ここで作成されたNotificationは端末の通知欄に表示されるため、バックグラウンド処理のように実行されつつも、何の処理がじっこうされているのかがユーザーに明示されます。
1 2 3 4 5 6 7 8 9 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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | class ForegroundService : Service(), TextToSpeech.OnInitListener { private var tts : TextToSpeech? = null override fun onBind(intent: Intent?): IBinder? { return null } override fun onCreate() { super.onCreate() tts = TextToSpeech(this, this) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val context = applicationContext val channelId = "tts_channel" val title = context.getString(R.string.app_name) val requestCode = 1 val pendingIntent = PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_ONE_SHOT) val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val channel = NotificationChannel(channelId, title, NotificationManager.IMPORTANCE_DEFAULT) notificationManager.createNotificationChannel(channel) // 通知作成 val notificationId = 1 val notification = Notification.Builder(context, channelId) .setContentTitle(title) .setSmallIcon(android.R.drawable.ic_btn_speak_now) .setContentText("テキスト読み上げ") .setAutoCancel(true) .setContentIntent(pendingIntent) .setWhen(System.currentTimeMillis()) .build() // foreground service実行 startForeground(notificationId, notification) // 読み上げ実行 val langLocale = intent!!.getSerializableExtra("langLocale") as Locale val inputText = intent.getStringExtra("input") speakText(langLocale, inputText) return START_NOT_STICKY } // テキスト読み上げ private fun speakText(langLocale:Locale, inputText:String) { // 読み上げる言語を設定する tts!!.language = langLocale // 10回読み上げる for (i in 1..10) { tts!!.speak(inputText, TextToSpeech.QUEUE_ADD, null, "speech1") } } override fun onDestroy() { super.onDestroy() stopSelf() } override fun onInit(status: Int) { if (status == TextToSpeech.SUCCESS) { Log.d("TTS", "TextToSpeechが初期化されました。") // 音声再生のイベントリスナを登録 val listener : SpeechListener? = SpeechListener() tts!!.setOnUtteranceProgressListener(listener) } else { Log.e("TTS", "TextToSpeechの初期化に失敗しました。") } } } |
実行してみる
入力したテキストを10回繰り返し読み上げますので、その最中に端末をスリープ状態にして、再生が途切れなければOKです。
(エミュレータの場合は、macであれば
⌘+P
でスリープできます)
さいごに
今回はTextToSpeechをサービスとして実行してみました。
ただ、OREO以上でのみ動作するコードですし、テキスト読み上げ完了のイベントを捕まえてforeground serviceを終了させるなどの処理が必要になると思います。
また、Serviceはテキスト読み上げ以外にも、音楽データの連続再生や、大きいサイズのデータのダウンロードなど、様々なケースで必要になりそうですので、引き続き調べていきます。