diff --git a/app/src/main/java/io/legado/app/constant/Bus.kt b/app/src/main/java/io/legado/app/constant/Bus.kt index ab7d44c84..4e90b7d82 100644 --- a/app/src/main/java/io/legado/app/constant/Bus.kt +++ b/app/src/main/java/io/legado/app/constant/Bus.kt @@ -3,4 +3,5 @@ package io.legado.app.constant object Bus { const val RECREATE = "RECREATE" const val UP_BOOK = "sourceDebugLog" + const val ALOUD_STATE = "aloud_state" } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/constant/Status.kt b/app/src/main/java/io/legado/app/constant/Status.kt new file mode 100644 index 000000000..605b37e51 --- /dev/null +++ b/app/src/main/java/io/legado/app/constant/Status.kt @@ -0,0 +1,5 @@ +package io.legado.app.constant + +object Status { + const val NEXT = 2 +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/help/PendingIntentHelp.kt b/app/src/main/java/io/legado/app/help/PendingIntentHelp.kt new file mode 100644 index 000000000..2105aa68e --- /dev/null +++ b/app/src/main/java/io/legado/app/help/PendingIntentHelp.kt @@ -0,0 +1,23 @@ +package io.legado.app.help + +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import io.legado.app.ui.readbook.ReadBookActivity + +object PendingIntentHelp { + + fun readBookActivityPendingIntent(context: Context): PendingIntent { + val intent = Intent(context, ReadBookActivity::class.java) + intent.action = "" + return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) + } + + fun aloudServicePendingIntent(context: Context, actionStr: String): PendingIntent { + val intent = Intent(context, this.javaClass) + intent.action = actionStr + return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) + } + + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/service/ReadAloudService.kt b/app/src/main/java/io/legado/app/service/ReadAloudService.kt index bbfa3525d..d2f2506b3 100644 --- a/app/src/main/java/io/legado/app/service/ReadAloudService.kt +++ b/app/src/main/java/io/legado/app/service/ReadAloudService.kt @@ -1,9 +1,8 @@ package io.legado.app.service import android.app.PendingIntent -import android.content.ComponentName -import android.content.Context -import android.content.Intent +import android.content.* +import android.graphics.BitmapFactory import android.media.AudioAttributes import android.media.AudioFocusRequest import android.media.AudioManager @@ -11,9 +10,16 @@ import android.os.Build import android.speech.tts.TextToSpeech import android.speech.tts.UtteranceProgressListener import android.support.v4.media.session.MediaSessionCompat +import android.support.v4.media.session.PlaybackStateCompat +import androidx.core.app.NotificationCompat import io.legado.app.R import io.legado.app.base.BaseService +import io.legado.app.constant.AppConst +import io.legado.app.constant.Bus +import io.legado.app.constant.Status +import io.legado.app.help.PendingIntentHelp import io.legado.app.receiver.MediaButtonIntentReceiver +import io.legado.app.utils.postEvent import io.legado.app.utils.toast import kotlinx.coroutines.launch import java.util.* @@ -21,8 +27,16 @@ import java.util.* class ReadAloudService : BaseService(), TextToSpeech.OnInitListener, AudioManager.OnAudioFocusChangeListener { companion object { - val tag = ReadAloudService::class.java.simpleName + val tag: String = ReadAloudService::class.java.simpleName var isRun = false + const val MEDIA_SESSION_ACTIONS = (PlaybackStateCompat.ACTION_PLAY + or PlaybackStateCompat.ACTION_PAUSE + or PlaybackStateCompat.ACTION_PLAY_PAUSE + or PlaybackStateCompat.ACTION_SKIP_TO_NEXT + or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS + or PlaybackStateCompat.ACTION_STOP + or PlaybackStateCompat.ACTION_SEEK_TO) + fun paly(context: Context, title: String, body: String) { } @@ -52,11 +66,20 @@ class ReadAloudService : BaseService(), TextToSpeech.OnInitListener, AudioManage } } + private val notificationId = 112201 private var textToSpeech: TextToSpeech? = null private var ttsIsSuccess: Boolean = false private lateinit var audioManager: AudioManager private lateinit var mFocusRequest: AudioFocusRequest private var mediaSessionCompat: MediaSessionCompat? = null + private var broadcastReceiver: BroadcastReceiver? = null + private var speak: Boolean = true + private var nowSpeak: Int = 0 + private val contentList = arrayListOf() + private var pause = false + private var title: String = "" + private var subtitle: String = "" + private var timeMinute: Int = 0 override fun onCreate() { super.onCreate() @@ -65,12 +88,15 @@ class ReadAloudService : BaseService(), TextToSpeech.OnInitListener, AudioManage audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager initFocusRequest() initMediaSession() - + initBroadcastReceiver() + upMediaSessionPlaybackState() + upNotification() } override fun onDestroy() { super.onDestroy() isRun = false + unregisterReceiver(broadcastReceiver) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -145,6 +171,37 @@ class ReadAloudService : BaseService(), TextToSpeech.OnInitListener, AudioManage } } + private fun initBroadcastReceiver() { + broadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val action = intent.action + if (AudioManager.ACTION_AUDIO_BECOMING_NOISY == action) { + pauseReadAloud(true) + } + } + } + val intentFilter = IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY) + registerReceiver(broadcastReceiver, intentFilter) + } + + private fun pauseReadAloud(pause: Boolean) { + textToSpeech?.stop() + } + + private fun resumeReadAloud() { + + } + + private fun playTTS() { + if (contentList.size < 1) { + postEvent(Bus.ALOUD_STATE, Status.NEXT) + return + } + if (ttsIsSuccess && !speak && requestFocus()) { + + } + } + /** * @return 音频焦点 */ @@ -162,6 +219,18 @@ class ReadAloudService : BaseService(), TextToSpeech.OnInitListener, AudioManage return request == AudioManager.AUDIOFOCUS_REQUEST_GRANTED } + private fun upMediaSessionPlaybackState() { + mediaSessionCompat?.setPlaybackState( + PlaybackStateCompat.Builder() + .setActions(MEDIA_SESSION_ACTIONS) + .setState( + if (speak) PlaybackStateCompat.STATE_PLAYING else PlaybackStateCompat.STATE_PAUSED, + nowSpeak.toLong(), 1f + ) + .build() + ) + } + override fun onAudioFocusChange(focusChange: Int) { when (focusChange) { AudioManager.AUDIOFOCUS_GAIN -> { @@ -191,6 +260,58 @@ class ReadAloudService : BaseService(), TextToSpeech.OnInitListener, AudioManage } } + /** + * 更新通知 + */ + private fun upNotification() { + var nTitle: String = when { + pause -> getString(R.string.read_aloud_pause) + timeMinute in 1..60 -> getString(R.string.read_aloud_timer, timeMinute) + else -> getString(R.string.read_aloud_t) + } + nTitle += ": $title" + if (subtitle.isEmpty()) + subtitle = getString(R.string.read_aloud_s) + val builder = NotificationCompat.Builder(this, AppConst.channelIdReadAloud) + .setSmallIcon(R.drawable.ic_volume_up) + .setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.icon_read_book)) + .setOngoing(true) + .setContentTitle(nTitle) + .setContentText(subtitle) + .setContentIntent(PendingIntentHelp.readBookActivityPendingIntent(this)) + if (pause) { + builder.addAction( + R.drawable.ic_play_24dp, + getString(R.string.resume), + PendingIntentHelp.aloudServicePendingIntent(this, "resume") + ) + } else { + builder.addAction( + R.drawable.ic_pause_24dp, + getString(R.string.pause), + PendingIntentHelp.aloudServicePendingIntent(this, "pause") + ) + } + builder.addAction( + R.drawable.ic_stop_black_24dp, + getString(R.string.stop), + PendingIntentHelp.aloudServicePendingIntent(this, "stop") + ) + builder.addAction( + R.drawable.ic_time_add_24dp, + getString(R.string.set_timer), + PendingIntentHelp.aloudServicePendingIntent(this, "setTimer") + ) + builder.setStyle( + androidx.media.app.NotificationCompat.MediaStyle() + .setMediaSession(mediaSessionCompat?.sessionToken) + .setShowActionsInCompactView(0, 1, 2) + ) + builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + val notification = builder.build() + startForeground(notificationId, notification) + } + /** * 朗读监听 */ @@ -208,10 +329,6 @@ class ReadAloudService : BaseService(), TextToSpeech.OnInitListener, AudioManage } - override fun onRangeStart(utteranceId: String, start: Int, end: Int, frame: Int) { - super.onRangeStart(utteranceId, start, end, frame) - - } } } \ No newline at end of file