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.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
@@ -55,8 +54,6 @@ class MainActivity : AppCompatActivity() {
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) {
@@ -66,7 +63,7 @@ class MainActivity : AppCompatActivity() {
startActivity(intent)
}
}
snackbar.view.layoutParams = params
snackbar.setAnchorView(findViewById(R.id.bottom_sheet))
snackbar.show()
}
}
@@ -14,7 +14,6 @@ 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
@@ -150,7 +149,11 @@ class PlayerFragment : Fragment(),
pickSingleMediaLauncher =
registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { imageUri ->
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 {
collection = CollectionHelper.setStationImageWithStationUuid(
activity as Context,
@@ -501,11 +504,17 @@ class PlayerFragment : Fragment(),
// display the TimePicker dialog
timePicker.show(requireActivity().supportFragmentManager, "tag")
}
else -> Snackbar.make(
else -> {
val snackbar = Snackbar.make(
requireView(),
R.string.toastmessage_sleep_timer_unable_to_start,
Snackbar.LENGTH_SHORT
).show()
)
if (!isAndroidTV) {
snackbar.setAnchorView(layout.bottomSheet)
}
snackbar.show()
}
}
}
@@ -860,9 +869,7 @@ class PlayerFragment : Fragment(),
R.color.default_neutral_white))
if (!isAndroidTV) {
val params = snackbar.view.layoutParams as FrameLayout.LayoutParams
params.bottomMargin = 300
snackbar.view.layoutParams = params
snackbar.setAnchorView(layout.bottomSheet)
}
snackbar.show()
}
@@ -42,7 +42,7 @@ data class LayoutHolder(var rootView: View) {
/* Main class variables */
var recyclerView: RecyclerView = rootView.findViewById(R.id.station_list)
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 sleepTimerRunningViews: Group? = rootView.findViewById(R.id.sleep_timer_running_views)