fix: ensure foreground service + notification permission on Android 14
- startService() before bindService() so Android 14 allows foreground promotion (bind-only no longer satisfies the "started from foreground" requirement since API 34) - smarter POST_NOTIFICATIONS flow: re-ask once, then redirect to app notification settings instead of silently doing nothing after denial - track first-ask via SharedPreferences so the settings redirect only triggers on a second-or-later launch after denial Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
0e17d2e5db
commit
0ee2a62adb
1 file changed
+50
-6
@@ -2,9 +2,12 @@ package com.novasonic.app
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.KeyEvent
|
||||
import android.webkit.WebChromeClient
|
||||
import android.webkit.WebResourceRequest
|
||||
@@ -37,6 +40,10 @@ class MainActivity : AppCompatActivity(), MusicService.StateListener {
|
||||
webBridge = WebBridge { mediaController }
|
||||
configureWebView()
|
||||
requestNotificationPermission()
|
||||
// Explicitly start the service before binding so Android 14 lets it
|
||||
// promote to foreground (bindService alone doesn't satisfy the
|
||||
// "started from foreground" requirement for foreground services).
|
||||
startService(Intent(this, MusicService::class.java))
|
||||
connectToService()
|
||||
|
||||
val url = BuildConfig.NOVASONIC_URL.ifEmpty { "https://demo.navidrome.org" }
|
||||
@@ -176,13 +183,50 @@ class MainActivity : AppCompatActivity(), MusicService.StateListener {
|
||||
// ─── Permissions ──────────────────────────────────────────────────────────
|
||||
|
||||
private fun requestNotificationPermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return
|
||||
when {
|
||||
ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
|
||||
!= PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 42
|
||||
)
|
||||
== PackageManager.PERMISSION_GRANTED -> return
|
||||
|
||||
// System won't show the dialog again after one denial — send user to settings.
|
||||
!ActivityCompat.shouldShowRequestPermissionRationale(
|
||||
this, Manifest.permission.POST_NOTIFICATIONS
|
||||
) && _notifPermissionAskedBefore() -> openNotificationSettings()
|
||||
|
||||
else -> {
|
||||
markNotifPermissionAsked()
|
||||
ActivityCompat.requestPermissions(
|
||||
this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 42
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int, permissions: Array<out String>, grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == 42 &&
|
||||
grantResults.firstOrNull() == PackageManager.PERMISSION_DENIED
|
||||
) {
|
||||
// Denied — open settings so the user can flip the toggle themselves.
|
||||
openNotificationSettings()
|
||||
}
|
||||
}
|
||||
|
||||
private fun openNotificationSettings() {
|
||||
startActivity(
|
||||
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||
data = Uri.fromParts("package", packageName, null)
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun _notifPermissionAskedBefore() =
|
||||
getSharedPreferences("ns_prefs", MODE_PRIVATE).getBoolean("notif_asked", false)
|
||||
|
||||
private fun markNotifPermissionAsked() =
|
||||
getSharedPreferences("ns_prefs", MODE_PRIVATE).edit()
|
||||
.putBoolean("notif_asked", true).apply()
|
||||
}
|
||||
Reference in new issue
Block a user