mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 15:29:15 +01:00
open now playing when the media notification is tapped
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package com.subtracks
|
package com.subtracks
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@@ -8,11 +9,14 @@ import androidx.activity.ComponentActivity
|
|||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.subtracks.ui.SubtracksRoot
|
import com.subtracks.ui.SubtracksRoot
|
||||||
import com.subtracks.ui.theme.SubtracksTheme
|
import com.subtracks.ui.theme.SubtracksTheme
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
private val nowPlayingRequest = mutableIntStateOf(0)
|
||||||
|
|
||||||
private val requestNotifications =
|
private val requestNotifications =
|
||||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { }
|
registerForActivityResult(ActivityResultContracts.RequestPermission()) { }
|
||||||
|
|
||||||
@@ -20,13 +24,20 @@ class MainActivity : ComponentActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
requestNotificationPermission()
|
requestNotificationPermission()
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
|
if (intent?.action == ACTION_OPEN_NOW_PLAYING) nowPlayingRequest.intValue++
|
||||||
setContent {
|
setContent {
|
||||||
SubtracksTheme {
|
SubtracksTheme {
|
||||||
SubtracksRoot()
|
SubtracksRoot(nowPlayingRequest = nowPlayingRequest.intValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onNewIntent(intent: Intent) {
|
||||||
|
super.onNewIntent(intent)
|
||||||
|
setIntent(intent)
|
||||||
|
if (intent.action == ACTION_OPEN_NOW_PLAYING) nowPlayingRequest.intValue++
|
||||||
|
}
|
||||||
|
|
||||||
private fun requestNotificationPermission() {
|
private fun requestNotificationPermission() {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return
|
||||||
val granted =
|
val granted =
|
||||||
@@ -34,4 +45,8 @@ class MainActivity : ComponentActivity() {
|
|||||||
PackageManager.PERMISSION_GRANTED
|
PackageManager.PERMISSION_GRANTED
|
||||||
if (!granted) requestNotifications.launch(Manifest.permission.POST_NOTIFICATIONS)
|
if (!granted) requestNotifications.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ACTION_OPEN_NOW_PLAYING = "com.subtracks.action.OPEN_NOW_PLAYING"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,7 +129,8 @@ class PlaybackService : MediaSessionService() {
|
|||||||
PendingIntent.getActivity(
|
PendingIntent.getActivity(
|
||||||
this,
|
this,
|
||||||
0,
|
0,
|
||||||
Intent(this, MainActivity::class.java),
|
Intent(this, MainActivity::class.java)
|
||||||
|
.setAction(MainActivity.ACTION_OPEN_NOW_PLAYING),
|
||||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
|
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
|
||||||
),
|
),
|
||||||
).setBitmapLoader(loader)
|
).setBitmapLoader(loader)
|
||||||
|
|||||||
@@ -166,6 +166,12 @@ internal fun resetOnSourceSwitch(
|
|||||||
if (route in DETAIL_ROUTES) popDetail()
|
if (route in DETAIL_ROUTES) popDetail()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun shouldOpenNowPlaying(
|
||||||
|
request: Int,
|
||||||
|
handled: Int,
|
||||||
|
playerVisible: Boolean,
|
||||||
|
): Boolean = playerVisible && request > handled
|
||||||
|
|
||||||
internal data class BackStackKey(
|
internal data class BackStackKey(
|
||||||
val route: String?,
|
val route: String?,
|
||||||
val argument: String?,
|
val argument: String?,
|
||||||
@@ -181,7 +187,10 @@ internal fun focusPops(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SubtracksRoot(root: RootViewModel = koinViewModel()) {
|
fun SubtracksRoot(
|
||||||
|
root: RootViewModel = koinViewModel(),
|
||||||
|
nowPlayingRequest: Int = 0,
|
||||||
|
) {
|
||||||
when (val hasSource = root.hasSource.collectAsStateWithLifecycle().value) {
|
when (val hasSource = root.hasSource.collectAsStateWithLifecycle().value) {
|
||||||
null -> {
|
null -> {
|
||||||
LoadingState()
|
LoadingState()
|
||||||
@@ -195,14 +204,14 @@ fun SubtracksRoot(root: RootViewModel = koinViewModel()) {
|
|||||||
val playbackController = koinInject<PlaybackController>()
|
val playbackController = koinInject<PlaybackController>()
|
||||||
LaunchedEffect(Unit) { playbackController.connect() }
|
LaunchedEffect(Unit) { playbackController.connect() }
|
||||||
val playbackReady by playbackController.ready.collectAsStateWithLifecycle()
|
val playbackReady by playbackController.ready.collectAsStateWithLifecycle()
|
||||||
if (playbackReady) MainNavigation() else LoadingState()
|
if (playbackReady) MainNavigation(nowPlayingRequest) else LoadingState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun MainNavigation() {
|
private fun MainNavigation(nowPlayingRequest: Int) {
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
val currentRoute =
|
val currentRoute =
|
||||||
navController
|
navController
|
||||||
@@ -218,6 +227,7 @@ private fun MainNavigation() {
|
|||||||
val downloads by downloadRepository.states().collectAsStateWithLifecycle()
|
val downloads by downloadRepository.states().collectAsStateWithLifecycle()
|
||||||
var showingQueue by rememberSaveable { mutableStateOf(false) }
|
var showingQueue by rememberSaveable { mutableStateOf(false) }
|
||||||
var nowPlayingOpen by rememberSaveable { mutableStateOf(false) }
|
var nowPlayingOpen by rememberSaveable { mutableStateOf(false) }
|
||||||
|
var handledNowPlayingRequest by rememberSaveable { mutableStateOf(0) }
|
||||||
var nowPlayingProgress by remember { mutableFloatStateOf(0f) }
|
var nowPlayingProgress by remember { mutableFloatStateOf(0f) }
|
||||||
var miniPlayerTopPx by remember { mutableFloatStateOf(0f) }
|
var miniPlayerTopPx by remember { mutableFloatStateOf(0f) }
|
||||||
var nowPlayingFadeOut by remember { mutableStateOf(false) }
|
var nowPlayingFadeOut by remember { mutableStateOf(false) }
|
||||||
@@ -302,6 +312,12 @@ private fun MainNavigation() {
|
|||||||
nowPlayingProgress = 0f
|
nowPlayingProgress = 0f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LaunchedEffect(nowPlayingRequest, playerVisible) {
|
||||||
|
if (shouldOpenNowPlaying(nowPlayingRequest, handledNowPlayingRequest, playerVisible)) {
|
||||||
|
handledNowPlayingRequest = nowPlayingRequest
|
||||||
|
settleNowPlaying(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
val navBarInset = with(density) { WindowInsets.navigationBars.getBottom(this).toDp() }
|
val navBarInset = with(density) { WindowInsets.navigationBars.getBottom(this).toDp() }
|
||||||
val keyboardUp = WindowInsets.imeAnimationTarget.getBottom(density) > 0
|
val keyboardUp = WindowInsets.imeAnimationTarget.getBottom(density) > 0
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.subtracks.ui
|
||||||
|
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class NowPlayingRequestTest {
|
||||||
|
@Test
|
||||||
|
fun aNotificationWithNothingPlayingDoesNotOpen() {
|
||||||
|
assertFalse(shouldOpenNowPlaying(request = 1, handled = 0, playerVisible = false))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun aNotificationWithSomethingPlayingOpens() {
|
||||||
|
assertTrue(shouldOpenNowPlaying(request = 1, handled = 0, playerVisible = true))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun anAlreadyHandledRequestDoesNotReopen() {
|
||||||
|
assertFalse(shouldOpenNowPlaying(request = 1, handled = 1, playerVisible = true))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun aNewerRequestOpensAgain() {
|
||||||
|
assertTrue(shouldOpenNowPlaying(request = 2, handled = 1, playerVisible = true))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun anOldRequestWaitsUntilSomethingIsPlaying() {
|
||||||
|
assertFalse(shouldOpenNowPlaying(request = 1, handled = 0, playerVisible = false))
|
||||||
|
assertTrue(shouldOpenNowPlaying(request = 1, handled = 0, playerVisible = true))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user