Migrate library display settings to compose

This commit is contained in:
Niels van Velzen authored and Niels van Velzen committed 2025-12-31 11:50:38 +01:00
1 parent d38627cb24
commit 00125664ac
12 files changed
+354 -94

No files matched your search

@@ -39,7 +39,6 @@ import org.jellyfin.androidtv.constant.QueryType;
import org.jellyfin.androidtv.data.model.FilterOptions;
import org.jellyfin.androidtv.data.querying.GetUserViewsRequest;
import org.jellyfin.androidtv.data.repository.CustomMessageRepository;
import org.jellyfin.androidtv.data.repository.UserViewsRepository;
import org.jellyfin.androidtv.data.service.BackgroundService;
import org.jellyfin.androidtv.databinding.HorizontalGridBrowseBinding;
import org.jellyfin.androidtv.databinding.PopupEmptyBinding;
@@ -50,8 +49,6 @@ import org.jellyfin.androidtv.ui.itemhandling.BaseRowItem;
import org.jellyfin.androidtv.ui.itemhandling.ItemLauncher;
import org.jellyfin.androidtv.ui.itemhandling.ItemRowAdapter;
import org.jellyfin.androidtv.ui.itemhandling.ItemRowAdapterHelperKt;
import org.jellyfin.androidtv.ui.navigation.ActivityDestinations;
import org.jellyfin.androidtv.ui.navigation.NavigationRepository;
import org.jellyfin.androidtv.ui.presentation.CardPresenter;
import org.jellyfin.androidtv.ui.presentation.HorizontalGridPresenter;
import org.jellyfin.androidtv.util.CoroutineUtils;
@@ -74,6 +71,7 @@ import java.util.Objects;
import java.util.UUID;
import kotlin.Lazy;
import kotlinx.coroutines.flow.MutableStateFlow;
import kotlinx.serialization.json.Json;
import timber.log.Timber;
@@ -115,9 +113,7 @@ public class BrowseGridFragment extends Fragment implements View.OnKeyListener {
private final Lazy<BackgroundService> backgroundService = inject(BackgroundService.class);
private final Lazy<PreferencesRepository> preferencesRepository = inject(PreferencesRepository.class);
private final Lazy<UserViewsRepository> userViewsRepository = inject(UserViewsRepository.class);
private final Lazy<CustomMessageRepository> customMessageRepository = inject(CustomMessageRepository.class);
private final Lazy<NavigationRepository> navigationRepository = inject(NavigationRepository.class);
private final Lazy<ItemLauncher> itemLauncher = inject(ItemLauncher.class);
private final Lazy<KeyProcessor> keyProcessor = inject(KeyProcessor.class);
private final Lazy<ApiClient> api = inject(ApiClient.class);
@@ -807,19 +803,22 @@ public class BrowseGridFragment extends Fragment implements View.OnKeyListener {
mLetterButton.setContentDescription(getString(R.string.lbl_by_letter));
binding.toolBar.addView(mLetterButton);
mSettingsButton = new ImageButton(requireContext(), null, 0, R.style.Button_Icon);
mSettingsButton.setImageResource(R.drawable.ic_settings);
mSettingsButton.setMaxHeight(size);
mSettingsButton.setAdjustViewBounds(true);
mSettingsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean allowViewSelection = userViewsRepository.getValue().allowViewSelection(mFolder.getCollectionType());
startActivity(ActivityDestinations.INSTANCE.displayPreferences(getContext(), mFolder.getDisplayPreferencesId(), allowViewSelection));
}
});
mSettingsButton.setContentDescription(getString(R.string.lbl_settings));
binding.toolBar.addView(mSettingsButton);
if (mFolder.getDisplayPreferencesId() != null) {
MutableStateFlow<Boolean> settingsVisible = BrowseGridFragmentHelperKt.createSettingsVisibility(BrowseGridFragment.this);
mSettingsButton = new ImageButton(requireContext(), null, 0, R.style.Button_Icon);
mSettingsButton.setImageResource(R.drawable.ic_settings);
mSettingsButton.setMaxHeight(size);
mSettingsButton.setAdjustViewBounds(true);
mSettingsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
settingsVisible.setValue(true);
}
});
mSettingsButton.setContentDescription(getString(R.string.lbl_settings));
binding.toolBar.addView(mSettingsButton);
BrowseGridFragmentHelperKt.addSettings(BrowseGridFragment.this, binding.settings, mFolder.getId(), mFolder.getDisplayPreferencesId(), settingsVisible);
}
}
class JumplistPopup {
@@ -0,0 +1,40 @@
package org.jellyfin.androidtv.ui.browsing
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.ComposeView
import kotlinx.coroutines.flow.MutableStateFlow
import org.jellyfin.androidtv.ui.base.JellyfinTheme
import org.jellyfin.androidtv.ui.navigation.ProvideRouter
import org.jellyfin.androidtv.ui.navigation.RouterContent
import org.jellyfin.androidtv.ui.settings.Routes
import org.jellyfin.androidtv.ui.settings.composable.SettingsDialog
import org.jellyfin.androidtv.ui.settings.routes
import java.util.UUID
fun BrowseGridFragment.createSettingsVisibility() = MutableStateFlow(false)
fun BrowseGridFragment.addSettings(
view: ComposeView,
itemId: UUID,
displayPreferencesId: String,
visible: MutableStateFlow<Boolean>,
) {
view.setContent {
val isVisible by visible.collectAsState(false)
JellyfinTheme {
ProvideRouter(routes, Routes.LIBRARIES_DISPLAY, mapOf("itemId" to itemId.toString(), "displayPreferencesId" to displayPreferencesId)) {
SettingsDialog(
visible = isVisible,
onDismissRequest = {
visible.value = false
onResume()
}
) {
RouterContent()
}
}
}
}
}
@@ -1,61 +0,0 @@
package org.jellyfin.androidtv.ui.browsing
import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.constant.GridDirection
import org.jellyfin.androidtv.constant.ImageType
import org.jellyfin.androidtv.constant.PosterSize
import org.jellyfin.androidtv.preference.LibraryPreferences
import org.jellyfin.androidtv.preference.PreferencesRepository
import org.jellyfin.androidtv.ui.preference.dsl.OptionsFragment
import org.jellyfin.androidtv.ui.preference.dsl.checkbox
import org.jellyfin.androidtv.ui.preference.dsl.enum
import org.jellyfin.androidtv.ui.preference.dsl.optionsScreen
import org.jellyfin.preference.store.PreferenceStore
import org.koin.android.ext.android.inject
class DisplayPreferencesScreen : OptionsFragment() {
private val preferencesRepository: PreferencesRepository by inject()
private val libraryPreferences: LibraryPreferences by lazy {
preferencesRepository.getLibraryPreferences(preferencesId!!)
}
private val preferencesId by lazy { requireArguments().getString(ARG_PREFERENCES_ID) }
private val allowViewSelection by lazy { requireArguments().getBoolean(ARG_ALLOW_VIEW_SELECTION) }
override val stores: Array<PreferenceStore<*, *>>
get() = arrayOf(libraryPreferences)
override val screen by optionsScreen {
setTitle(R.string.lbl_display_preferences)
category {
enum<PosterSize> {
setTitle(R.string.lbl_image_size)
bind(libraryPreferences, LibraryPreferences.posterSize)
}
enum<ImageType> {
setTitle(R.string.lbl_image_type)
bind(libraryPreferences, LibraryPreferences.imageType)
}
enum<GridDirection> {
setTitle(R.string.grid_direction)
bind(libraryPreferences, LibraryPreferences.gridDirection)
}
if (allowViewSelection) {
checkbox {
setTitle(R.string.enable_smart_view)
contentOn = requireContext().getString(R.string.enable_smart_view_description)
contentOff = contentOn
bind(libraryPreferences, LibraryPreferences.enableSmartScreen)
}
}
}
}
companion object {
const val ARG_ALLOW_VIEW_SELECTION = "allow_view_selection"
const val ARG_PREFERENCES_ID = "preferences_id"
}
}
@@ -3,7 +3,6 @@ package org.jellyfin.androidtv.ui.navigation
import android.content.Context
import android.content.Intent
import androidx.core.os.bundleOf
import org.jellyfin.androidtv.ui.browsing.DisplayPreferencesScreen
import org.jellyfin.androidtv.ui.livetv.GuideFiltersScreen
import org.jellyfin.androidtv.ui.livetv.GuideOptionsScreen
import org.jellyfin.androidtv.ui.playback.ExternalPlayerActivity
@@ -28,13 +27,6 @@ object ActivityDestinations {
fun playbackAdvancedPreferences(context: Context) = preferenceIntent<PlaybackAdvancedPreferencesScreen>(context)
fun displayPreferences(context: Context, displayPreferencesId: String, allowViewSelection: Boolean) =
preferenceIntent<DisplayPreferencesScreen>(
context,
DisplayPreferencesScreen.ARG_PREFERENCES_ID to displayPreferencesId,
DisplayPreferencesScreen.ARG_ALLOW_VIEW_SELECTION to allowViewSelection,
)
fun liveTvGuideFilterPreferences(context: Context) = preferenceIntent<GuideFiltersScreen>(context)
fun liveTvGuideOptionPreferences(context: Context) = preferenceIntent<GuideOptionsScreen>(context)
@@ -21,6 +21,10 @@ import org.jellyfin.androidtv.ui.settings.screen.customization.subtitle.Settings
import org.jellyfin.androidtv.ui.settings.screen.customization.subtitle.SettingsSubtitlesTextColorScreen
import org.jellyfin.androidtv.ui.settings.screen.home.SettingsHomeScreen
import org.jellyfin.androidtv.ui.settings.screen.home.SettingsHomeSectionScreen
import org.jellyfin.androidtv.ui.settings.screen.library.SettingsLibrariesDisplayGridScreen
import org.jellyfin.androidtv.ui.settings.screen.library.SettingsLibrariesDisplayImageSizeScreen
import org.jellyfin.androidtv.ui.settings.screen.library.SettingsLibrariesDisplayImageTypeScreen
import org.jellyfin.androidtv.ui.settings.screen.library.SettingsLibrariesDisplayScreen
import org.jellyfin.androidtv.ui.settings.screen.library.SettingsLibrariesScreen
import org.jellyfin.androidtv.ui.settings.screen.license.SettingsLicenseScreen
import org.jellyfin.androidtv.ui.settings.screen.license.SettingsLicensesScreen
@@ -59,6 +63,10 @@ object Routes {
const val CUSTOMIZATION_SUBTITLES_BACKGROUND_COLOR = "/customization/subtitles/background-color"
const val CUSTOMIZATION_SUBTITLES_EDGE_COLOR = "/customization/subtitles/edge-color"
const val LIBRARIES = "/libraries"
const val LIBRARIES_DISPLAY = "/libraries/display/{itemId}/{displayPreferencesId}"
const val LIBRARIES_DISPLAY_IMAGE_SIZE = "/libraries/display/{itemId}/{displayPreferencesId}/image-size"
const val LIBRARIES_DISPLAY_IMAGE_TYPE = "/libraries/display/{itemId}/{displayPreferencesId}/image-type"
const val LIBRARIES_DISPLAY_GRID = "/libraries/display/{itemId}/{displayPreferencesId}/grid"
const val HOME = "/home"
const val HOME_SECTION = "/home/section/{index}"
const val PLAYBACK = "/playback"
@@ -142,6 +150,18 @@ val routes = mapOf<String, RouteComposable>(
Routes.LIBRARIES to {
SettingsLibrariesScreen()
},
Routes.LIBRARIES_DISPLAY to { context ->
SettingsLibrariesDisplayScreen(context.parameters["itemId"]?.toUUIDOrNull()!!, context.parameters["displayPreferencesId"]!!)
},
Routes.LIBRARIES_DISPLAY_IMAGE_SIZE to { context ->
SettingsLibrariesDisplayImageSizeScreen(context.parameters["itemId"]?.toUUIDOrNull()!!, context.parameters["displayPreferencesId"]!!)
},
Routes.LIBRARIES_DISPLAY_IMAGE_TYPE to { context ->
SettingsLibrariesDisplayImageTypeScreen(context.parameters["itemId"]?.toUUIDOrNull()!!, context.parameters["displayPreferencesId"]!!)
},
Routes.LIBRARIES_DISPLAY_GRID to { context ->
SettingsLibrariesDisplayGridScreen(context.parameters["itemId"]?.toUUIDOrNull()!!, context.parameters["displayPreferencesId"]!!)
},
Routes.HOME to {
SettingsHomeScreen()
},
@@ -0,0 +1,50 @@
package org.jellyfin.androidtv.ui.settings.screen.library
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.constant.GridDirection
import org.jellyfin.androidtv.preference.LibraryPreferences
import org.jellyfin.androidtv.preference.PreferencesRepository
import org.jellyfin.androidtv.ui.base.Text
import org.jellyfin.androidtv.ui.base.form.RadioButton
import org.jellyfin.androidtv.ui.base.list.ListButton
import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.navigation.LocalRouter
import org.jellyfin.androidtv.ui.settings.compat.rememberPreference
import org.jellyfin.androidtv.ui.settings.composable.SettingsColumn
import org.koin.compose.koinInject
import java.util.UUID
@Composable
fun SettingsLibrariesDisplayGridScreen(itemId: UUID, displayPreferencesId: String) {
val router = LocalRouter.current
val preferencesRepository = koinInject<PreferencesRepository>()
val userView = rememberUserView(itemId)
val libraryPreferences = remember(displayPreferencesId) { preferencesRepository.getLibraryPreferences(displayPreferencesId) }
var gridDirection by rememberPreference(libraryPreferences, LibraryPreferences.gridDirection)
SettingsColumn {
item {
ListSection(
overlineContent = { Text(userView?.name.orEmpty().uppercase()) },
headingContent = { Text(stringResource(R.string.grid_direction)) },
)
}
items(GridDirection.entries) { entry ->
ListButton(
headingContent = { Text(stringResource(entry.nameRes)) },
trailingContent = { RadioButton(checked = gridDirection == entry) },
onClick = {
gridDirection = entry
router.back()
}
)
}
}
}
@@ -0,0 +1,50 @@
package org.jellyfin.androidtv.ui.settings.screen.library
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.constant.PosterSize
import org.jellyfin.androidtv.preference.LibraryPreferences
import org.jellyfin.androidtv.preference.PreferencesRepository
import org.jellyfin.androidtv.ui.base.Text
import org.jellyfin.androidtv.ui.base.form.RadioButton
import org.jellyfin.androidtv.ui.base.list.ListButton
import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.navigation.LocalRouter
import org.jellyfin.androidtv.ui.settings.compat.rememberPreference
import org.jellyfin.androidtv.ui.settings.composable.SettingsColumn
import org.koin.compose.koinInject
import java.util.UUID
@Composable
fun SettingsLibrariesDisplayImageSizeScreen(itemId: UUID, displayPreferencesId: String) {
val router = LocalRouter.current
val preferencesRepository = koinInject<PreferencesRepository>()
val userView = rememberUserView(itemId)
val libraryPreferences = remember(displayPreferencesId) { preferencesRepository.getLibraryPreferences(displayPreferencesId) }
var posterSize by rememberPreference(libraryPreferences, LibraryPreferences.posterSize)
SettingsColumn {
item {
ListSection(
overlineContent = { Text(userView?.name.orEmpty().uppercase()) },
headingContent = { Text(stringResource(R.string.lbl_image_size)) },
)
}
items(PosterSize.entries) { entry ->
ListButton(
headingContent = { Text(stringResource(entry.nameRes)) },
trailingContent = { RadioButton(checked = posterSize == entry) },
onClick = {
posterSize = entry
router.back()
}
)
}
}
}
@@ -0,0 +1,50 @@
package org.jellyfin.androidtv.ui.settings.screen.library
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.constant.ImageType
import org.jellyfin.androidtv.preference.LibraryPreferences
import org.jellyfin.androidtv.preference.PreferencesRepository
import org.jellyfin.androidtv.ui.base.Text
import org.jellyfin.androidtv.ui.base.form.RadioButton
import org.jellyfin.androidtv.ui.base.list.ListButton
import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.navigation.LocalRouter
import org.jellyfin.androidtv.ui.settings.compat.rememberPreference
import org.jellyfin.androidtv.ui.settings.composable.SettingsColumn
import org.koin.compose.koinInject
import java.util.UUID
@Composable
fun SettingsLibrariesDisplayImageTypeScreen(itemId: UUID, displayPreferencesId: String) {
val router = LocalRouter.current
val preferencesRepository = koinInject<PreferencesRepository>()
val userView = rememberUserView(itemId)
val libraryPreferences = remember(displayPreferencesId) { preferencesRepository.getLibraryPreferences(displayPreferencesId) }
var imageType by rememberPreference(libraryPreferences, LibraryPreferences.imageType)
SettingsColumn {
item {
ListSection(
overlineContent = { Text(userView?.name.orEmpty().uppercase()) },
headingContent = { Text(stringResource(R.string.lbl_image_type)) },
)
}
items(ImageType.entries) { entry ->
ListButton(
headingContent = { Text(stringResource(entry.nameRes)) },
trailingContent = { RadioButton(checked = imageType == entry) },
onClick = {
imageType = entry
router.back()
}
)
}
}
}
@@ -0,0 +1,97 @@
package org.jellyfin.androidtv.ui.settings.screen.library
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.data.repository.UserViewsRepository
import org.jellyfin.androidtv.preference.LibraryPreferences
import org.jellyfin.androidtv.preference.PreferencesRepository
import org.jellyfin.androidtv.ui.base.Text
import org.jellyfin.androidtv.ui.base.form.Checkbox
import org.jellyfin.androidtv.ui.base.list.ListButton
import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.navigation.LocalRouter
import org.jellyfin.androidtv.ui.settings.Routes
import org.jellyfin.androidtv.ui.settings.compat.rememberPreference
import org.jellyfin.androidtv.ui.settings.composable.SettingsColumn
import org.koin.compose.koinInject
import java.util.UUID
@Composable
fun SettingsLibrariesDisplayScreen(itemId: UUID, displayPreferencesId: String) {
val router = LocalRouter.current
val userViewsRepository = koinInject<UserViewsRepository>()
val preferencesRepository = koinInject<PreferencesRepository>()
val userView = rememberUserView(itemId)
val libraryPreferences = remember(displayPreferencesId) { preferencesRepository.getLibraryPreferences(displayPreferencesId) }
val allowViewSelection = userViewsRepository.allowViewSelection(userView?.collectionType)
SettingsColumn {
item {
ListSection(
overlineContent = { Text(stringResource(R.string.pref_libraries).uppercase()) },
headingContent = { Text(userView?.name.orEmpty()) },
)
}
item {
var posterSize by rememberPreference(libraryPreferences, LibraryPreferences.posterSize)
ListButton(
headingContent = { Text(stringResource(R.string.lbl_image_size)) },
captionContent = { Text(stringResource(posterSize.nameRes)) },
onClick = {
router.push(
Routes.LIBRARIES_DISPLAY_IMAGE_SIZE,
mapOf("itemId" to itemId.toString(), "displayPreferencesId" to displayPreferencesId)
)
}
)
}
item {
var imageType by rememberPreference(libraryPreferences, LibraryPreferences.imageType)
ListButton(
headingContent = { Text(stringResource(R.string.lbl_image_type)) },
captionContent = { Text(stringResource(imageType.nameRes)) },
onClick = {
router.push(
Routes.LIBRARIES_DISPLAY_IMAGE_TYPE,
mapOf("itemId" to itemId.toString(), "displayPreferencesId" to displayPreferencesId)
)
}
)
}
item {
var gridDirection by rememberPreference(libraryPreferences, LibraryPreferences.gridDirection)
ListButton(
headingContent = { Text(stringResource(R.string.grid_direction)) },
captionContent = { Text(stringResource(gridDirection.nameRes)) },
onClick = {
router.push(
Routes.LIBRARIES_DISPLAY_GRID,
mapOf("itemId" to itemId.toString(), "displayPreferencesId" to displayPreferencesId)
)
}
)
}
if (allowViewSelection) item {
var enableSmartScreen by rememberPreference(libraryPreferences, LibraryPreferences.enableSmartScreen)
ListButton(
headingContent = { Text(stringResource(R.string.enable_smart_view)) },
trailingContent = { Checkbox(checked = enableSmartScreen) },
captionContent = { Text(stringResource(R.string.enable_smart_view_description)) },
onClick = { enableSmartScreen = !enableSmartScreen }
)
}
}
}
@@ -16,6 +16,8 @@ import org.jellyfin.androidtv.ui.base.Text
import org.jellyfin.androidtv.ui.base.list.ListButton
import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.navigation.ActivityDestinations
import org.jellyfin.androidtv.ui.navigation.LocalRouter
import org.jellyfin.androidtv.ui.settings.Routes
import org.jellyfin.androidtv.ui.settings.composable.SettingsColumn
import org.jellyfin.sdk.model.api.CollectionType
import org.koin.compose.koinInject
@@ -23,6 +25,7 @@ import org.koin.compose.koinInject
@Composable
fun SettingsLibrariesScreen() {
val context = LocalContext.current
val router = LocalRouter.current
val userViewsRepository = koinInject<UserViewsRepository>()
val userViews by remember {
userViewsRepository.views.map { it.toList() }
@@ -37,7 +40,6 @@ fun SettingsLibrariesScreen() {
}
items(userViews) { userView ->
val allowViewSelection = userViewsRepository.allowViewSelection(userView.collectionType)
val allowGridView = userViewsRepository.allowGridView(userView.collectionType)
val displayPreferencesId = userView.displayPreferencesId
@@ -56,12 +58,9 @@ fun SettingsLibrariesScreen() {
enabled = canOpen,
onClick = {
if (canOpen) {
context.startActivity(
ActivityDestinations.displayPreferences(
context = context,
displayPreferencesId = displayPreferencesId,
allowViewSelection = allowViewSelection
)
router.push(
Routes.LIBRARIES_DISPLAY,
mapOf("itemId" to userView.id.toString(), "displayPreferencesId" to userView.displayPreferencesId!!)
)
}
}
@@ -0,0 +1,20 @@
package org.jellyfin.androidtv.ui.settings.screen.library
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import kotlinx.coroutines.flow.map
import org.jellyfin.androidtv.data.repository.UserViewsRepository
import org.jellyfin.sdk.model.api.BaseItemDto
import org.koin.compose.koinInject
import java.util.UUID
@Composable
fun rememberUserView(itemId: UUID): BaseItemDto? {
val userViewsRepository = koinInject<UserViewsRepository>()
val userView by remember {
userViewsRepository.views.map { views -> views.first { view -> view.id == itemId } }
}.collectAsState(null)
return userView
}
@@ -95,4 +95,8 @@
android:layout_marginTop="2dp"
android:layout_marginEnd="15dp" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/settings"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>