mirror of
https://github.com/Michatec/Radio.git
synced 2026-06-03 17:41:14 +02:00
feat(android): refine notification permissions and UI layout
Add explicit `POST_NOTIFICATIONS` permission checks in `SettingsFragment` and `PlayerFragment` before triggering notifications or showing related preferences. Update the permission launcher in `MainActivity` to provide a "Settings" action that links to the system application details when the notification permission is permanently denied. Improve UI and system integration by: * Adjusting `Snackbar` positioning with bottom margins in `MainActivity` and `PlayerFragment`. * Updating `NotificationSys` to use a new channel ID with `IMPORTANCE_LOW`. * Configuring `MainActivity` in `activity_main.xml` to handle orientation and screen size configuration changes manually.
This commit is contained in:
@@ -1,25 +1,30 @@
|
||||
package com.michatec.radio
|
||||
|
||||
import android.Manifest
|
||||
import android.os.Build
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.NavigationUI
|
||||
import androidx.navigation.ui.navigateUp
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.michatec.radio.helpers.AppThemeHelper
|
||||
import com.michatec.radio.helpers.FileHelper
|
||||
import com.michatec.radio.helpers.LanguageHelper
|
||||
@@ -45,11 +50,24 @@ class MainActivity : AppCompatActivity() {
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { isGranted ->
|
||||
if (!isGranted) {
|
||||
Snackbar.make(
|
||||
val snackbar = Snackbar.make(
|
||||
findViewById(android.R.id.content),
|
||||
R.string.snackbar_failed_permission_notification,
|
||||
R.string.snackbar_failed_permission_notification,
|
||||
Snackbar.LENGTH_LONG
|
||||
).show()
|
||||
)
|
||||
val params = snackbar.view.layoutParams as FrameLayout.LayoutParams
|
||||
params.bottomMargin = 300
|
||||
// If the user permanently denied the permission, show a link to settings
|
||||
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.POST_NOTIFICATIONS)) {
|
||||
snackbar.setAction(R.string.fragment_settings_title) {
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||
data = Uri.fromParts("package", packageName, null)
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
snackbar.view.layoutParams = params
|
||||
snackbar.show()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,21 +6,20 @@ import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.michatec.radio.R
|
||||
|
||||
object NotificationSys {
|
||||
private const val CHANNEL_ID = "com.michatec.radio.channel"
|
||||
private const val CHANNEL_ID = "com.michatec.radio.channel_messages"
|
||||
private const val CHANNEL_NAME = "Notifications"
|
||||
private const val NOTIFICATION_ID = 1001
|
||||
private const val NOTIFICATION_ID = 5000
|
||||
|
||||
fun createNotificationChannel(context: Context) {
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
if (notificationManager.getNotificationChannel(CHANNEL_ID) == null) {
|
||||
val channel = NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
CHANNEL_NAME,
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
CHANNEL_ID,
|
||||
CHANNEL_NAME,
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
).apply {
|
||||
description = context.getString(R.string.notification_channel_description)
|
||||
}
|
||||
@@ -37,9 +36,9 @@ object NotificationSys {
|
||||
}
|
||||
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
targetIntent,
|
||||
context,
|
||||
0,
|
||||
targetIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
@@ -54,10 +53,4 @@ object NotificationSys {
|
||||
|
||||
notificationManager.notify(NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
fun showNotification(context: Context, titleResId: Int, contentResId: Int, intent: Intent? = null) {
|
||||
val title = context.getString(titleResId)
|
||||
val content = context.getString(contentResId)
|
||||
showNotification(context, title, content, intent)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.michatec.radio
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
@@ -13,6 +14,7 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
@@ -60,7 +62,6 @@ import com.michatec.radio.extensions.*
|
||||
import com.michatec.radio.helpers.*
|
||||
import com.michatec.radio.ui.LayoutHolder
|
||||
import com.michatec.radio.ui.PlayerState
|
||||
import com.michatec.radio.BuildConfig
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
@@ -101,6 +102,10 @@ class PlayerFragment : Fragment(),
|
||||
context?.packageManager?.hasSystemFeature(PackageManager.FEATURE_LEANBACK) == true
|
||||
}
|
||||
|
||||
private fun isPermissionGranted(context: Context, permission: String): Boolean {
|
||||
return context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onCreate from Fragment */
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -840,7 +845,7 @@ class PlayerFragment : Fragment(),
|
||||
if (latestVersion != current && !BuildConfig.IS_DEBUG_ENABLED) {
|
||||
// We have an update available, tell our user about it
|
||||
view?.let {
|
||||
Snackbar.make(it, getString(R.string.app_name) + " " + latestVersion + " " + getString(R.string.snackbar_update_available), 10000)
|
||||
val snackbar = Snackbar.make(it, getString(R.string.app_name) + " " + latestVersion + " " + getString(R.string.snackbar_update_available), 10000)
|
||||
.setAction(R.string.snackbar_show) {
|
||||
val releaseurl = getString(R.string.snackbar_url_app_home_page)
|
||||
val i = Intent(Intent.ACTION_VIEW)
|
||||
@@ -853,17 +858,19 @@ class PlayerFragment : Fragment(),
|
||||
ContextCompat.getColor(
|
||||
requireActivity(),
|
||||
R.color.default_neutral_white))
|
||||
.show()
|
||||
}
|
||||
|
||||
if (!isAndroidTV) {
|
||||
if (!isAndroidTV) {
|
||||
val params = snackbar.view.layoutParams as FrameLayout.LayoutParams
|
||||
params.bottomMargin = 300
|
||||
snackbar.view.layoutParams = params
|
||||
}
|
||||
snackbar.show()
|
||||
}
|
||||
if (!isAndroidTV && isPermissionGranted(requireContext(), Manifest.permission.POST_NOTIFICATIONS)) {
|
||||
val releaseUrl = getString(R.string.snackbar_url_app_home_page)
|
||||
|
||||
// Create the clean browser intent that will trigger ONLY when the notification is tapped
|
||||
val updateIntent = Intent(Intent.ACTION_VIEW, Uri.parse(releaseUrl)).apply {
|
||||
val updateIntent = Intent(Intent.ACTION_VIEW, releaseUrl.toUri()).apply {
|
||||
putExtra("SOURCE", "SELF")
|
||||
}
|
||||
|
||||
NotificationSys.showNotification(
|
||||
requireContext(),
|
||||
"${getString(R.string.app_name)} $latestVersion",
|
||||
|
||||
@@ -22,7 +22,6 @@ import com.michatec.radio.dialogs.PresetSelectionDialog
|
||||
import com.michatec.radio.dialogs.ThemeSelectionDialog
|
||||
import com.michatec.radio.dialogs.YesNoDialog
|
||||
import com.michatec.radio.helpers.*
|
||||
import com.michatec.radio.NotificationSys
|
||||
import android.content.pm.PackageManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
@@ -45,6 +44,10 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||
context?.packageManager?.hasSystemFeature(PackageManager.FEATURE_LEANBACK) == true
|
||||
}
|
||||
|
||||
private fun isPermissionGranted(context: Context, permission: String): Boolean {
|
||||
return context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
/* Overrides onViewCreated from PreferenceFragmentCompat */
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
@@ -386,7 +389,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||
preferenceCategoryGeneral.addPreference(preferenceThemeSelection)
|
||||
preferenceCategoryGeneral.addPreference(preferenceLanguageSelection)
|
||||
|
||||
if (!isAndroidTV) {
|
||||
if (!isAndroidTV && isPermissionGranted(activity as Context, android.Manifest.permission.POST_NOTIFICATIONS)) {
|
||||
preferenceCategoryGeneral.addPreference(preferenceTestNotification)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user