committed by
crueter
10 changed files with 484 additions and 4 deletions
-
1src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
-
74src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/GpuUnswizzleSetting.kt
-
13src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
-
206src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/GpuUnswizzleDialogFragment.kt
-
15src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
-
4src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
-
71src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/GpuUnswizzleViewHolder.kt
-
96src/android/app/src/main/res/layout/dialog_gpu_unswizzle.xml
-
5src/android/app/src/main/res/values/strings.xml
-
3src/common/settings.h
@ -0,0 +1,74 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later |
||||
|
|
||||
|
package org.yuzu.yuzu_emu.features.settings.model.view |
||||
|
|
||||
|
import androidx.annotation.ArrayRes |
||||
|
import androidx.annotation.StringRes |
||||
|
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting |
||||
|
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting |
||||
|
import org.yuzu.yuzu_emu.features.settings.model.IntSetting |
||||
|
|
||||
|
class GpuUnswizzleSetting( |
||||
|
@StringRes titleId: Int = 0, |
||||
|
titleString: String = "", |
||||
|
@StringRes descriptionId: Int = 0, |
||||
|
descriptionString: String = "", |
||||
|
@ArrayRes val textureSizeChoicesId: Int, |
||||
|
@ArrayRes val textureSizeValuesId: Int, |
||||
|
@ArrayRes val streamSizeChoicesId: Int, |
||||
|
@ArrayRes val streamSizeValuesId: Int, |
||||
|
@ArrayRes val chunkSizeChoicesId: Int, |
||||
|
@ArrayRes val chunkSizeValuesId: Int |
||||
|
) : SettingsItem( |
||||
|
object : AbstractSetting { |
||||
|
override val key: String = "gpu_unswizzle_combined" |
||||
|
override val defaultValue: Any = false |
||||
|
override val isSaveable = true |
||||
|
override val isRuntimeModifiable = false |
||||
|
override fun getValueAsString(needsGlobal: Boolean): String = "combined" |
||||
|
override fun reset() { |
||||
|
BooleanSetting.GPU_UNSWIZZLE_ENABLED.reset() |
||||
|
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.reset() |
||||
|
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.reset() |
||||
|
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.reset() |
||||
|
} |
||||
|
}, |
||||
|
titleId, |
||||
|
titleString, |
||||
|
descriptionId, |
||||
|
descriptionString |
||||
|
) { |
||||
|
override val type = SettingsItem.TYPE_GPU_UNSWIZZLE |
||||
|
|
||||
|
// Check if GPU unswizzle is enabled via the dedicated boolean setting |
||||
|
fun isEnabled(needsGlobal: Boolean = false): Boolean = |
||||
|
BooleanSetting.GPU_UNSWIZZLE_ENABLED.getBoolean(needsGlobal) |
||||
|
|
||||
|
fun setEnabled(value: Boolean) = |
||||
|
BooleanSetting.GPU_UNSWIZZLE_ENABLED.setBoolean(value) |
||||
|
|
||||
|
fun enable() = setEnabled(true) |
||||
|
|
||||
|
fun disable() = setEnabled(false) |
||||
|
|
||||
|
fun getTextureSize(needsGlobal: Boolean = false): Int = |
||||
|
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.getInt(needsGlobal) |
||||
|
|
||||
|
fun setTextureSize(value: Int) = |
||||
|
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.setInt(value) |
||||
|
|
||||
|
fun getStreamSize(needsGlobal: Boolean = false): Int = |
||||
|
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.getInt(needsGlobal) |
||||
|
|
||||
|
fun setStreamSize(value: Int) = |
||||
|
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.setInt(value) |
||||
|
|
||||
|
fun getChunkSize(needsGlobal: Boolean = false): Int = |
||||
|
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.getInt(needsGlobal) |
||||
|
|
||||
|
fun setChunkSize(value: Int) = |
||||
|
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.setInt(value) |
||||
|
|
||||
|
fun reset() = setting.reset() |
||||
|
} |
||||
@ -0,0 +1,206 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later |
||||
|
|
||||
|
package org.yuzu.yuzu_emu.features.settings.ui |
||||
|
|
||||
|
import android.app.Dialog |
||||
|
import android.content.DialogInterface |
||||
|
import android.os.Bundle |
||||
|
import android.view.LayoutInflater |
||||
|
import android.widget.ArrayAdapter |
||||
|
import androidx.fragment.app.DialogFragment |
||||
|
import androidx.fragment.app.activityViewModels |
||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder |
||||
|
import org.yuzu.yuzu_emu.R |
||||
|
import org.yuzu.yuzu_emu.databinding.DialogGpuUnswizzleBinding |
||||
|
import org.yuzu.yuzu_emu.features.settings.model.view.GpuUnswizzleSetting |
||||
|
|
||||
|
class GpuUnswizzleDialogFragment : DialogFragment() { |
||||
|
private var position = 0 |
||||
|
private val settingsViewModel: SettingsViewModel by activityViewModels() |
||||
|
private lateinit var binding: DialogGpuUnswizzleBinding |
||||
|
|
||||
|
override fun onCreate(savedInstanceState: Bundle?) { |
||||
|
super.onCreate(savedInstanceState) |
||||
|
position = requireArguments().getInt(POSITION) |
||||
|
|
||||
|
if (settingsViewModel.clickedItem == null) dismiss() |
||||
|
} |
||||
|
|
||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { |
||||
|
binding = DialogGpuUnswizzleBinding.inflate(LayoutInflater.from(requireContext())) |
||||
|
val item = settingsViewModel.clickedItem as GpuUnswizzleSetting |
||||
|
|
||||
|
// Setup texture size dropdown |
||||
|
val textureSizeEntries = resources.getStringArray(item.textureSizeChoicesId) |
||||
|
val textureSizeValues = resources.getIntArray(item.textureSizeValuesId) |
||||
|
val textureSizeAdapter = ArrayAdapter( |
||||
|
requireContext(), |
||||
|
android.R.layout.simple_dropdown_item_1line, |
||||
|
textureSizeEntries.toMutableList() |
||||
|
) |
||||
|
binding.dropdownTextureSize.setAdapter(textureSizeAdapter) |
||||
|
|
||||
|
// Setup stream size dropdown |
||||
|
val streamSizeEntries = resources.getStringArray(item.streamSizeChoicesId) |
||||
|
val streamSizeValues = resources.getIntArray(item.streamSizeValuesId) |
||||
|
val streamSizeAdapter = ArrayAdapter( |
||||
|
requireContext(), |
||||
|
android.R.layout.simple_dropdown_item_1line, |
||||
|
streamSizeEntries.toMutableList() |
||||
|
) |
||||
|
binding.dropdownStreamSize.setAdapter(streamSizeAdapter) |
||||
|
|
||||
|
// Setup chunk size dropdown |
||||
|
val chunkSizeEntries = resources.getStringArray(item.chunkSizeChoicesId) |
||||
|
val chunkSizeValues = resources.getIntArray(item.chunkSizeValuesId) |
||||
|
val chunkSizeAdapter = ArrayAdapter( |
||||
|
requireContext(), |
||||
|
android.R.layout.simple_dropdown_item_1line, |
||||
|
chunkSizeEntries.toMutableList() |
||||
|
) |
||||
|
binding.dropdownChunkSize.setAdapter(chunkSizeAdapter) |
||||
|
|
||||
|
// Load current values |
||||
|
val isEnabled = item.isEnabled() |
||||
|
binding.switchEnable.isChecked = isEnabled |
||||
|
|
||||
|
if (isEnabled) { |
||||
|
val textureSizeIndex = textureSizeValues.indexOf(item.getTextureSize()) |
||||
|
if (textureSizeIndex >= 0) { |
||||
|
binding.dropdownTextureSize.setText(textureSizeEntries[textureSizeIndex], false) |
||||
|
} |
||||
|
|
||||
|
val streamSizeIndex = streamSizeValues.indexOf(item.getStreamSize()) |
||||
|
if (streamSizeIndex >= 0) { |
||||
|
binding.dropdownStreamSize.setText(streamSizeEntries[streamSizeIndex], false) |
||||
|
} |
||||
|
|
||||
|
val chunkSizeIndex = chunkSizeValues.indexOf(item.getChunkSize()) |
||||
|
if (chunkSizeIndex >= 0) { |
||||
|
binding.dropdownChunkSize.setText(chunkSizeEntries[chunkSizeIndex], false) |
||||
|
} |
||||
|
} else { |
||||
|
// Set default/recommended values when disabling |
||||
|
binding.dropdownTextureSize.setText(textureSizeEntries[3], false) |
||||
|
binding.dropdownStreamSize.setText(streamSizeEntries[3], false) |
||||
|
binding.dropdownChunkSize.setText(chunkSizeEntries[3], false) |
||||
|
} |
||||
|
|
||||
|
// Clear adapter filters after setText to fix rotation bug |
||||
|
textureSizeAdapter.filter.filter(null) |
||||
|
streamSizeAdapter.filter.filter(null) |
||||
|
chunkSizeAdapter.filter.filter(null) |
||||
|
|
||||
|
// Enable/disable dropdowns based on switch state |
||||
|
updateDropdownsState(isEnabled) |
||||
|
binding.switchEnable.setOnCheckedChangeListener { _, checked -> |
||||
|
updateDropdownsState(checked) |
||||
|
} |
||||
|
|
||||
|
val dialog = MaterialAlertDialogBuilder(requireContext()) |
||||
|
.setTitle(item.title) |
||||
|
.setView(binding.root) |
||||
|
.create() |
||||
|
|
||||
|
// Setup button listeners |
||||
|
binding.btnDefault.setOnClickListener { |
||||
|
// Reset to defaults |
||||
|
item.reset() |
||||
|
// Refresh values with adapters reset |
||||
|
val textureSizeIndex = textureSizeValues.indexOf(item.getTextureSize()) |
||||
|
if (textureSizeIndex >= 0) { |
||||
|
binding.dropdownTextureSize.setText(textureSizeEntries[textureSizeIndex], false) |
||||
|
} |
||||
|
val streamSizeIndex = streamSizeValues.indexOf(item.getStreamSize()) |
||||
|
if (streamSizeIndex >= 0) { |
||||
|
binding.dropdownStreamSize.setText(streamSizeEntries[streamSizeIndex], false) |
||||
|
} |
||||
|
val chunkSizeIndex = chunkSizeValues.indexOf(item.getChunkSize()) |
||||
|
if (chunkSizeIndex >= 0) { |
||||
|
binding.dropdownChunkSize.setText(chunkSizeEntries[chunkSizeIndex], false) |
||||
|
} |
||||
|
// Clear filters |
||||
|
textureSizeAdapter.filter.filter(null) |
||||
|
streamSizeAdapter.filter.filter(null) |
||||
|
chunkSizeAdapter.filter.filter(null) |
||||
|
|
||||
|
settingsViewModel.setAdapterItemChanged(position) |
||||
|
settingsViewModel.setShouldReloadSettingsList(true) |
||||
|
} |
||||
|
|
||||
|
binding.btnCancel.setOnClickListener { |
||||
|
dialog.dismiss() |
||||
|
} |
||||
|
|
||||
|
binding.btnOk.setOnClickListener { |
||||
|
if (binding.switchEnable.isChecked) { |
||||
|
item.enable() |
||||
|
// Save the selected values |
||||
|
val selectedTextureIndex = textureSizeEntries.indexOf( |
||||
|
binding.dropdownTextureSize.text.toString() |
||||
|
) |
||||
|
if (selectedTextureIndex >= 0) { |
||||
|
item.setTextureSize(textureSizeValues[selectedTextureIndex]) |
||||
|
} |
||||
|
|
||||
|
val selectedStreamIndex = streamSizeEntries.indexOf( |
||||
|
binding.dropdownStreamSize.text.toString() |
||||
|
) |
||||
|
if (selectedStreamIndex >= 0) { |
||||
|
item.setStreamSize(streamSizeValues[selectedStreamIndex]) |
||||
|
} |
||||
|
|
||||
|
val selectedChunkIndex = chunkSizeEntries.indexOf( |
||||
|
binding.dropdownChunkSize.text.toString() |
||||
|
) |
||||
|
if (selectedChunkIndex >= 0) { |
||||
|
item.setChunkSize(chunkSizeValues[selectedChunkIndex]) |
||||
|
} |
||||
|
} else { |
||||
|
// Disable GPU unswizzle |
||||
|
item.disable() |
||||
|
} |
||||
|
|
||||
|
settingsViewModel.setAdapterItemChanged(position) |
||||
|
settingsViewModel.setShouldReloadSettingsList(true) |
||||
|
dialog.dismiss() |
||||
|
} |
||||
|
|
||||
|
// Ensure filters are cleared after dialog is shown |
||||
|
binding.root.post { |
||||
|
textureSizeAdapter.filter.filter(null) |
||||
|
streamSizeAdapter.filter.filter(null) |
||||
|
chunkSizeAdapter.filter.filter(null) |
||||
|
} |
||||
|
|
||||
|
return dialog |
||||
|
} |
||||
|
|
||||
|
private fun updateDropdownsState(enabled: Boolean) { |
||||
|
binding.layoutTextureSize.isEnabled = enabled |
||||
|
binding.dropdownTextureSize.isEnabled = enabled |
||||
|
binding.layoutStreamSize.isEnabled = enabled |
||||
|
binding.dropdownStreamSize.isEnabled = enabled |
||||
|
binding.layoutChunkSize.isEnabled = enabled |
||||
|
binding.dropdownChunkSize.isEnabled = enabled |
||||
|
} |
||||
|
|
||||
|
companion object { |
||||
|
const val TAG = "GpuUnswizzleDialogFragment" |
||||
|
const val POSITION = "Position" |
||||
|
|
||||
|
fun newInstance( |
||||
|
settingsViewModel: SettingsViewModel, |
||||
|
item: GpuUnswizzleSetting, |
||||
|
position: Int |
||||
|
): GpuUnswizzleDialogFragment { |
||||
|
val dialog = GpuUnswizzleDialogFragment() |
||||
|
val args = Bundle() |
||||
|
args.putInt(POSITION, position) |
||||
|
dialog.arguments = args |
||||
|
settingsViewModel.clickedItem = item |
||||
|
return dialog |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,71 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later |
||||
|
|
||||
|
package org.yuzu.yuzu_emu.features.settings.ui.viewholder |
||||
|
|
||||
|
import android.view.View |
||||
|
import org.yuzu.yuzu_emu.R |
||||
|
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding |
||||
|
import org.yuzu.yuzu_emu.features.settings.model.view.GpuUnswizzleSetting |
||||
|
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem |
||||
|
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter |
||||
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible |
||||
|
|
||||
|
class GpuUnswizzleViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : |
||||
|
SettingViewHolder(binding.root, adapter) { |
||||
|
private lateinit var setting: GpuUnswizzleSetting |
||||
|
|
||||
|
override fun bind(item: SettingsItem) { |
||||
|
setting = item as GpuUnswizzleSetting |
||||
|
binding.textSettingName.text = setting.title |
||||
|
binding.textSettingDescription.setVisible(item.description.isNotEmpty()) |
||||
|
binding.textSettingDescription.text = item.description |
||||
|
|
||||
|
binding.textSettingValue.setVisible(true) |
||||
|
val resMgr = binding.root.context.resources |
||||
|
|
||||
|
if (setting.isEnabled()) { |
||||
|
// Show a summary of current settings |
||||
|
val textureSizeEntries = resMgr.getStringArray(setting.textureSizeChoicesId) |
||||
|
val textureSizeValues = resMgr.getIntArray(setting.textureSizeValuesId) |
||||
|
val textureSizeIndex = textureSizeValues.indexOf(setting.getTextureSize()) |
||||
|
val textureSizeLabel = if (textureSizeIndex >= 0) textureSizeEntries[textureSizeIndex] else "?" |
||||
|
|
||||
|
val streamSizeEntries = resMgr.getStringArray(setting.streamSizeChoicesId) |
||||
|
val streamSizeValues = resMgr.getIntArray(setting.streamSizeValuesId) |
||||
|
val streamSizeIndex = streamSizeValues.indexOf(setting.getStreamSize()) |
||||
|
val streamSizeLabel = if (streamSizeIndex >= 0) streamSizeEntries[streamSizeIndex] else "?" |
||||
|
|
||||
|
val chunkSizeEntries = resMgr.getStringArray(setting.chunkSizeChoicesId) |
||||
|
val chunkSizeValues = resMgr.getIntArray(setting.chunkSizeValuesId) |
||||
|
val chunkSizeIndex = chunkSizeValues.indexOf(setting.getChunkSize()) |
||||
|
val chunkSizeLabel = if (chunkSizeIndex >= 0) chunkSizeEntries[chunkSizeIndex] else "?" |
||||
|
|
||||
|
binding.textSettingValue.text = "$textureSizeLabel ⋅ $streamSizeLabel ⋅ $chunkSizeLabel" |
||||
|
} else { |
||||
|
binding.textSettingValue.text = resMgr.getString(R.string.gpu_unswizzle_disabled) |
||||
|
} |
||||
|
|
||||
|
binding.buttonClear.setVisible(setting.clearable) |
||||
|
binding.buttonClear.setOnClickListener { |
||||
|
adapter.onClearClick(setting, bindingAdapterPosition) |
||||
|
} |
||||
|
|
||||
|
setStyle(setting.isEditable, binding) |
||||
|
} |
||||
|
|
||||
|
override fun onClick(clicked: View) { |
||||
|
if (!setting.isEditable) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
adapter.onGpuUnswizzleClick(setting, bindingAdapterPosition) |
||||
|
} |
||||
|
|
||||
|
override fun onLongClick(clicked: View): Boolean { |
||||
|
if (setting.isEditable) { |
||||
|
return adapter.onLongClick(setting, bindingAdapterPosition) |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,96 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:scrollbars="vertical"> |
||||
|
|
||||
|
<LinearLayout |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:orientation="vertical" |
||||
|
android:padding="24dp"> |
||||
|
|
||||
|
<com.google.android.material.switchmaterial.SwitchMaterial |
||||
|
android:id="@+id/switch_enable" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginBottom="16dp" |
||||
|
android:text="@string/gpu_unswizzle_enable" /> |
||||
|
|
||||
|
<com.google.android.material.textfield.TextInputLayout |
||||
|
android:id="@+id/layout_texture_size" |
||||
|
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginBottom="12dp" |
||||
|
android:hint="@string/gpu_unswizzle_texture_size"> |
||||
|
|
||||
|
<com.google.android.material.textfield.MaterialAutoCompleteTextView |
||||
|
android:id="@+id/dropdown_texture_size" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:inputType="none" /> |
||||
|
</com.google.android.material.textfield.TextInputLayout> |
||||
|
|
||||
|
<com.google.android.material.textfield.TextInputLayout |
||||
|
android:id="@+id/layout_stream_size" |
||||
|
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginBottom="12dp" |
||||
|
android:hint="@string/gpu_unswizzle_stream_size"> |
||||
|
|
||||
|
<com.google.android.material.textfield.MaterialAutoCompleteTextView |
||||
|
android:id="@+id/dropdown_stream_size" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:inputType="none" /> |
||||
|
</com.google.android.material.textfield.TextInputLayout> |
||||
|
|
||||
|
<com.google.android.material.textfield.TextInputLayout |
||||
|
android:id="@+id/layout_chunk_size" |
||||
|
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:hint="@string/gpu_unswizzle_chunk_size"> |
||||
|
|
||||
|
<com.google.android.material.textfield.MaterialAutoCompleteTextView |
||||
|
android:id="@+id/dropdown_chunk_size" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:inputType="none" /> |
||||
|
</com.google.android.material.textfield.TextInputLayout> |
||||
|
|
||||
|
<LinearLayout |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginTop="24dp" |
||||
|
android:orientation="horizontal" |
||||
|
android:gravity="end" |
||||
|
android:spacing="8dp"> |
||||
|
|
||||
|
<com.google.android.material.button.MaterialButton |
||||
|
android:id="@+id/btn_default" |
||||
|
style="@style/Widget.Material3.Button.TextButton" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:text="@string/gpu_unswizzle_default_button" /> |
||||
|
|
||||
|
<com.google.android.material.button.MaterialButton |
||||
|
android:id="@+id/btn_cancel" |
||||
|
style="@style/Widget.Material3.Button.TextButton" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:text="@android:string/cancel" /> |
||||
|
|
||||
|
<com.google.android.material.button.MaterialButton |
||||
|
android:id="@+id/btn_ok" |
||||
|
style="@style/Widget.Material3.Button.TextButton" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:text="@android:string/ok" /> |
||||
|
</LinearLayout> |
||||
|
|
||||
|
</LinearLayout> |
||||
|
</ScrollView> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue