Files
Radio/app/src/main/java/com/michatec/radio/MainActivity.kt
T
Michatec 83752c356f 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.
2026-05-31 19:27:51 +02:00

189 lines
6.8 KiB
Kotlin

package com.michatec.radio
import android.Manifest
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.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
import com.michatec.radio.helpers.PreferencesHelper
import org.woheller69.freeDroidWarn.FreeDroidWarn
import java.util.Locale
/*
* MainActivity class
*/
class MainActivity : AppCompatActivity() {
/* Main class variables */
private lateinit var appBarConfiguration: AppBarConfiguration
// Check if the device running the app is an Android TV instance
private val isAndroidTV: Boolean by lazy {
packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
}
// request notification permission (for Android 13+)
private val permissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (!isGranted) {
val snackbar = Snackbar.make(
findViewById(android.R.id.content),
R.string.snackbar_failed_permission_notification,
Snackbar.LENGTH_LONG
)
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()
}
}
/* Overrides attachBaseContext from AppCompatActivity */
override fun attachBaseContext(newBase: Context) {
val languageCode = PreferencesHelper.loadSelectedLanguage()
val context = if (languageCode.isEmpty() || languageCode == "system") {
// Use system default locale
newBase
} else {
val locale = Locale.forLanguageTag(languageCode)
val config = Configuration(newBase.resources.configuration)
config.setLocale(locale)
newBase.createConfigurationContext(config)
}
super.attachBaseContext(context)
}
/* Overrides onCreate from AppCompatActivity */
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
setTheme(R.style.AppTheme)
super.onCreate(savedInstanceState)
// Free Android
FreeDroidWarn.showWarningOnUpgrade(this, BuildConfig.VERSION_CODE)
// set up views
setContentView(R.layout.activity_main)
// create .nomedia file - if not yet existing
FileHelper.createNomediaFile(getExternalFilesDir(null))
// set up action bar
setSupportActionBar(findViewById(R.id.main_toolbar))
val toolbar: Toolbar = findViewById(R.id.main_toolbar)
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.main_host_container) as NavHostFragment
val navController = navHostFragment.navController
appBarConfiguration = AppBarConfiguration(navController.graph)
NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration)
supportActionBar?.hide()
// TV-specific loading logic: Hide the overlay once the app is ready
if (isAndroidTV) {
Handler(Looper.getMainLooper()).postDelayed({
hideLoadingOverlay()
}, 1200)
} else {
findViewById<View>(R.id.loading_layout)?.visibility = View.GONE
}
// register listener for changes in shared preferences
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
// request permissions
if (!isAndroidTV && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
/* Hides the loading/splash overlay */
private fun hideLoadingOverlay() {
findViewById<View>(R.id.loading_layout)?.let { overlay ->
if (overlay.isVisible) {
overlay.animate().alpha(0f).setDuration(500)
.withEndAction { overlay.visibility = View.GONE }
}
}
}
/* Overrides onResume from AppCompatActivity */
override fun onResume() {
try {
super.onResume()
} catch (_: ClassCastException) {
// Do nothing
}
}
/* Overrides onSupportNavigateUp from AppCompatActivity */
override fun onSupportNavigateUp(): Boolean {
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.main_host_container) as NavHostFragment
val navController = navHostFragment.navController
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
/* Overrides onDestroy from AppCompatActivity */
override fun onDestroy() {
super.onDestroy()
// unregister listener for changes in shared preferences
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
}
/*
* Defines the listener for changes in shared preferences
*/
private val sharedPreferenceChangeListener =
SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
when (key) {
Keys.PREF_THEME_SELECTION -> {
AppThemeHelper.setTheme(PreferencesHelper.loadThemeSelection())
}
Keys.PREF_LANGUAGE_SELECTED -> {
LanguageHelper.setLanguage(this, PreferencesHelper.loadSelectedLanguage())
}
}
}
/*
* End of declaration
*/
}