refactor(ui): use anchor view for Snackbars above bottom sheet

Replace manual bottom margin adjustments with `setAnchorView` for Snackbars to ensure they are positioned correctly above the bottom sheet on mobile devices.

- In `LayoutHolder`, make `bottomSheet` public to allow access from other components.
- In `PlayerFragment` and `MainActivity`, replace `FrameLayout.LayoutParams` modifications with `setAnchorView(bottomSheet)`.
- Maintain standard Snackbar behavior for Android TV where the bottom sheet is not present.
This commit is contained in:
2026-05-31 23:06:57 +02:00
parent 83752c356f
commit 4429ed4057
3 changed files with 19 additions and 15 deletions
@@ -13,7 +13,6 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import android.provider.Settings import android.provider.Settings
import android.view.View import android.view.View
import android.widget.FrameLayout
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@@ -55,8 +54,6 @@ class MainActivity : AppCompatActivity() {
R.string.snackbar_failed_permission_notification, R.string.snackbar_failed_permission_notification,
Snackbar.LENGTH_LONG 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 the user permanently denied the permission, show a link to settings
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.POST_NOTIFICATIONS)) { if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.POST_NOTIFICATIONS)) {
snackbar.setAction(R.string.fragment_settings_title) { snackbar.setAction(R.string.fragment_settings_title) {
@@ -66,7 +63,7 @@ class MainActivity : AppCompatActivity() {
startActivity(intent) startActivity(intent)
} }
} }
snackbar.view.layoutParams = params snackbar.setAnchorView(findViewById(R.id.bottom_sheet))
snackbar.show() snackbar.show()
} }
} }
@@ -14,7 +14,6 @@ import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.Toast import android.widget.Toast
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
@@ -150,7 +149,11 @@ class PlayerFragment : Fragment(),
pickSingleMediaLauncher = pickSingleMediaLauncher =
registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { imageUri -> registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { imageUri ->
if (imageUri == null) { if (imageUri == null) {
Snackbar.make(requireView(), R.string.toastalert_failed_picking_media, Snackbar.LENGTH_LONG).show() val snackbar = Snackbar.make(requireView(), R.string.toastalert_failed_picking_media, Snackbar.LENGTH_LONG)
if (!isAndroidTV) {
snackbar.setAnchorView(layout.bottomSheet)
}
snackbar.show()
} else { } else {
collection = CollectionHelper.setStationImageWithStationUuid( collection = CollectionHelper.setStationImageWithStationUuid(
activity as Context, activity as Context,
@@ -501,11 +504,17 @@ class PlayerFragment : Fragment(),
// display the TimePicker dialog // display the TimePicker dialog
timePicker.show(requireActivity().supportFragmentManager, "tag") timePicker.show(requireActivity().supportFragmentManager, "tag")
} }
else -> Snackbar.make( else -> {
requireView(), val snackbar = Snackbar.make(
R.string.toastmessage_sleep_timer_unable_to_start, requireView(),
Snackbar.LENGTH_SHORT R.string.toastmessage_sleep_timer_unable_to_start,
).show() Snackbar.LENGTH_SHORT
)
if (!isAndroidTV) {
snackbar.setAnchorView(layout.bottomSheet)
}
snackbar.show()
}
} }
} }
@@ -860,9 +869,7 @@ class PlayerFragment : Fragment(),
R.color.default_neutral_white)) R.color.default_neutral_white))
if (!isAndroidTV) { if (!isAndroidTV) {
val params = snackbar.view.layoutParams as FrameLayout.LayoutParams snackbar.setAnchorView(layout.bottomSheet)
params.bottomMargin = 300
snackbar.view.layoutParams = params
} }
snackbar.show() snackbar.show()
} }
@@ -42,7 +42,7 @@ data class LayoutHolder(var rootView: View) {
/* Main class variables */ /* Main class variables */
var recyclerView: RecyclerView = rootView.findViewById(R.id.station_list) var recyclerView: RecyclerView = rootView.findViewById(R.id.station_list)
val layoutManager: LinearLayoutManager val layoutManager: LinearLayoutManager
private var bottomSheet: ConstraintLayout? = rootView.findViewById(R.id.bottom_sheet) var bottomSheet: ConstraintLayout? = rootView.findViewById(R.id.bottom_sheet)
//private var sheetMetadataViews: Group //private var sheetMetadataViews: Group
private var sleepTimerRunningViews: Group? = rootView.findViewById(R.id.sleep_timer_running_views) private var sleepTimerRunningViews: Group? = rootView.findViewById(R.id.sleep_timer_running_views)