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
+47
-3
@@ -2,9 +2,12 @@ package com.novasonic.app
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.provider.Settings
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.webkit.WebChromeClient
|
import android.webkit.WebChromeClient
|
||||||
import android.webkit.WebResourceRequest
|
import android.webkit.WebResourceRequest
|
||||||
@@ -37,6 +40,10 @@ class MainActivity : AppCompatActivity(), MusicService.StateListener {
|
|||||||
webBridge = WebBridge { mediaController }
|
webBridge = WebBridge { mediaController }
|
||||||
configureWebView()
|
configureWebView()
|
||||||
requestNotificationPermission()
|
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()
|
connectToService()
|
||||||
|
|
||||||
val url = BuildConfig.NOVASONIC_URL.ifEmpty { "https://demo.navidrome.org" }
|
val url = BuildConfig.NOVASONIC_URL.ifEmpty { "https://demo.navidrome.org" }
|
||||||
@@ -176,13 +183,50 @@ class MainActivity : AppCompatActivity(), MusicService.StateListener {
|
|||||||
// ─── Permissions ──────────────────────────────────────────────────────────
|
// ─── Permissions ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
private fun requestNotificationPermission() {
|
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)
|
ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
|
||||||
!= PackageManager.PERMISSION_GRANTED
|
== 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(
|
ActivityCompat.requestPermissions(
|
||||||
this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 42
|
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