Skip to content

Commit 559a4fa

Browse files
committed
[feature] Support pausing playback when audio switches to the speaker
1 parent 9c41fb8 commit 559a4fa

File tree

7 files changed

+68
-58
lines changed

7 files changed

+68
-58
lines changed

app/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ android {
2222
minSdk = 24
2323
targetSdk = 35
2424
versionCode = 25
25-
versionName = "2.1-rc03"
25+
versionName = "2.1-rc04"
2626

2727
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2828

app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@
4646
android:theme="@style/Theme.AniVu.Pink"
4747
tools:targetApi="31">
4848
<profileable
49-
android:shell="true"
5049
android:enabled="true"
50+
android:shell="true"
5151
tools:targetApi="29" />
5252

5353
<receiver
@@ -178,11 +178,7 @@
178178
<service
179179
android:name=".ui.mpv.service.PlayerService"
180180
android:exported="false"
181-
android:foregroundServiceType="mediaPlayback"/>
182-
183-
<receiver
184-
android:name=".ui.mpv.service.PlayerNotificationReceiver"
185-
android:exported="false" />
181+
android:foregroundServiceType="mediaPlayback" />
186182

187183
<provider
188184
android:name="androidx.core.content.FileProvider"

app/src/main/java/com/skyd/anivu/ui/activity/player/PlayActivity.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,9 @@ import com.skyd.anivu.ext.getOrDefault
2929
import com.skyd.anivu.ext.savePictureToMediaStore
3030
import com.skyd.anivu.model.preference.player.BackgroundPlayPreference
3131
import com.skyd.anivu.ui.component.showToast
32-
import com.skyd.anivu.ui.mpv.service.PlayerNotificationReceiver
33-
import com.skyd.anivu.ui.mpv.service.PlayerService
3432
import com.skyd.anivu.ui.mpv.PlayerViewRoute
3533
import com.skyd.anivu.ui.mpv.copyAssetsForMpv
34+
import com.skyd.anivu.ui.mpv.service.PlayerService
3635
import java.io.File
3736

3837

@@ -85,7 +84,7 @@ class PlayActivity : BaseComposeActivity() {
8584
private val serviceStopReceiver = object : BroadcastReceiver() {
8685
override fun onReceive(context: Context?, intent: Intent?) {
8786
if (context == null || intent == null) return
88-
if (intent.action == PlayerNotificationReceiver.FINISH_PLAY_ACTIVITY_ACTION) {
87+
if (intent.action == PlayerService.FINISH_PLAY_ACTIVITY_ACTION) {
8988
finish()
9089
}
9190
}
@@ -104,7 +103,7 @@ class PlayActivity : BaseComposeActivity() {
104103
ContextCompat.registerReceiver(
105104
this,
106105
serviceStopReceiver,
107-
IntentFilter(PlayerNotificationReceiver.FINISH_PLAY_ACTIVITY_ACTION),
106+
IntentFilter(PlayerService.FINISH_PLAY_ACTIVITY_ACTION),
108107
ContextCompat.RECEIVER_NOT_EXPORTED
109108
)
110109

app/src/main/java/com/skyd/anivu/ui/mpv/PlayerCommand.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ sealed interface PlayerCommand {
1414
data class Detach(val surface: Surface) : PlayerCommand
1515
data class SetUri(val uri: Uri) : PlayerCommand
1616
data object Destroy : PlayerCommand
17-
data class Paused(val paused: Boolean, val uri: Uri) : PlayerCommand
17+
data class Paused(val paused: Boolean, val uri: Uri?) : PlayerCommand
1818
data object PlayOrPause : PlayerCommand
1919
data class SeekTo(val position: Int) : PlayerCommand
2020
data class Rotate(val rotate: Int) : PlayerCommand

app/src/main/java/com/skyd/anivu/ui/mpv/service/PlayerNotificationManager.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class PlayerNotificationManager(
5353
title: CharSequence,
5454
intentAction: String,
5555
): NotificationCompat.Action {
56-
val intent = PlayerNotificationReceiver.createIntent(context, intentAction)
56+
val intent = PlayerService.createIntent(context, intentAction)
5757
val builder = NotificationCompat.Action.Builder(icon, title, intent)
5858
with(builder) {
5959
setContextual(false)
@@ -81,16 +81,16 @@ class PlayerNotificationManager(
8181
if (sessionManager.state != PlaybackStateCompat.STATE_PLAYING) buildNotificationAction(
8282
icon = R.drawable.ic_play_arrow_24,
8383
title = context.getString(R.string.play),
84-
intentAction = PlayerNotificationReceiver.PLAY_ACTION
84+
intentAction = PlayerService.PLAY_ACTION
8585
) else buildNotificationAction(
8686
icon = R.drawable.ic_pause_24,
8787
title = context.getString(R.string.pause),
88-
intentAction = PlayerNotificationReceiver.PLAY_ACTION
88+
intentAction = PlayerService.PLAY_ACTION
8989
)
9090
val closePendingIntent = buildNotificationAction(
9191
icon = R.drawable.ic_close_24,
9292
title = context.getString(R.string.close),
93-
intentAction = PlayerNotificationReceiver.CLOSE_ACTION
93+
intentAction = PlayerService.CLOSE_ACTION
9494
)
9595
addAction(playPendingIntent)
9696
addAction(closePendingIntent)

app/src/main/java/com/skyd/anivu/ui/mpv/service/PlayerNotificationReceiver.kt

Lines changed: 0 additions & 39 deletions
This file was deleted.

app/src/main/java/com/skyd/anivu/ui/mpv/service/PlayerService.kt

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,23 @@ package com.skyd.anivu.ui.mpv.service
33
import android.app.Application
44
import android.app.ForegroundServiceStartNotAllowedException
55
import android.app.Notification
6+
import android.app.PendingIntent
67
import android.app.Service
8+
import android.content.BroadcastReceiver
9+
import android.content.Context
710
import android.content.Intent
11+
import android.content.IntentFilter
812
import android.content.pm.ServiceInfo
13+
import android.media.AudioManager
914
import android.net.Uri
1015
import android.os.Binder
1116
import android.os.Build
1217
import android.os.IBinder
1318
import android.support.v4.media.session.MediaSessionCompat
1419
import android.support.v4.media.session.PlaybackStateCompat
1520
import androidx.core.app.ServiceCompat
21+
import androidx.core.content.ContextCompat
22+
import com.skyd.anivu.BuildConfig
1623
import com.skyd.anivu.appContext
1724
import com.skyd.anivu.model.bean.MediaPlayHistoryBean
1825
import com.skyd.anivu.model.repository.PlayerRepository
@@ -40,12 +47,13 @@ class PlayerService : Service() {
4047

4148
private val lifecycleScope = CoroutineScope(Dispatchers.Main)
4249

50+
private val playerNotificationReceiver = PlayerNotificationReceiver()
4351
private val binder = PlayerServiceBinder()
4452
var uri: Uri = Uri.EMPTY
4553
private set
54+
val player = MPVPlayer.getInstance(appContext as Application)
4655
private val sessionManager = MediaSessionManager(appContext, createMediaSessionCallback())
4756
private val notificationManager = PlayerNotificationManager(appContext, sessionManager)
48-
val player = MPVPlayer.getInstance(appContext as Application)
4957
val playerState get() = sessionManager.playerState
5058

5159
private val observers = mutableSetOf<Observer>()
@@ -110,7 +118,11 @@ class PlayerService : Service() {
110118
override fun event(eventId: Int) {
111119
when (eventId) {
112120
MPVLib.mpvEventId.MPV_EVENT_SEEK -> sendEvent(PlayerEvent.Seek)
113-
MPVLib.mpvEventId.MPV_EVENT_END_FILE -> sendEvent(PlayerEvent.EndFile)
121+
MPVLib.mpvEventId.MPV_EVENT_END_FILE -> {
122+
sendEvent(PlayerEvent.EndFile)
123+
savePosition()
124+
}
125+
114126
MPVLib.mpvEventId.MPV_EVENT_FILE_LOADED -> {
115127
sendEvent(PlayerEvent.FileLoaded)
116128
sendEvent(PlayerEvent.Paused(player.paused))
@@ -137,6 +149,18 @@ class PlayerService : Service() {
137149

138150
override fun onCreate() {
139151
super.onCreate()
152+
153+
ContextCompat.registerReceiver(
154+
this,
155+
playerNotificationReceiver,
156+
IntentFilter().apply {
157+
addAction(PLAY_ACTION)
158+
addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY)
159+
addAction(CLOSE_ACTION)
160+
},
161+
ContextCompat.RECEIVER_NOT_EXPORTED,
162+
)
163+
140164
addObserver(sessionManager)
141165
MPVLib.addObserver(mpvObserver)
142166
notificationManager.createNotificationChannel()
@@ -159,6 +183,8 @@ class PlayerService : Service() {
159183
notificationManager.cancel()
160184
removeAllObserver()
161185
lifecycleScope.cancel()
186+
187+
unregisterReceiver(playerNotificationReceiver)
162188
super.onDestroy()
163189
}
164190

@@ -203,7 +229,7 @@ class PlayerService : Service() {
203229
if (keepOpen && eofReached) {
204230
seek(0)
205231
} else if (isIdling) {
206-
command.uri.resolveUri(this@PlayerService)?.let { loadFile(it) }
232+
command.uri!!.resolveUri(this@PlayerService)?.let { loadFile(it) }
207233
}
208234
}
209235
paused = command.paused
@@ -287,7 +313,35 @@ class PlayerService : Service() {
287313
}
288314
}
289315

316+
inner class PlayerNotificationReceiver : BroadcastReceiver() {
317+
override fun onReceive(context: Context?, intent: Intent?) {
318+
if (intent == null) return
319+
320+
when (intent.action) {
321+
AudioManager.ACTION_AUDIO_BECOMING_NOISY ->
322+
onCommand(PlayerCommand.Paused(true, null))
323+
324+
PLAY_ACTION -> onCommand(PlayerCommand.PlayOrPause)
325+
CLOSE_ACTION -> {
326+
onCommand(PlayerCommand.Destroy)
327+
context?.sendBroadcast(Intent(FINISH_PLAY_ACTIVITY_ACTION))
328+
}
329+
}
330+
}
331+
}
332+
290333
companion object {
291334
private val scope = CoroutineScope(Dispatchers.IO)
335+
336+
const val PLAY_ACTION = BuildConfig.APPLICATION_ID + ".PlayerPlay"
337+
const val CLOSE_ACTION = BuildConfig.APPLICATION_ID + ".PlayerClose"
338+
const val FINISH_PLAY_ACTIVITY_ACTION = BuildConfig.APPLICATION_ID + ".FinishPlayActivity"
339+
340+
fun createIntent(context: Context, action: String): PendingIntent {
341+
val intent = Intent(action).apply {
342+
setPackage(BuildConfig.APPLICATION_ID)
343+
}
344+
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
345+
}
292346
}
293347
}

0 commit comments

Comments
 (0)