Fix customization menu navigation and layout issues: - Fix D-pad navigation between theme and block skin selectors - Prevent word wrapping in customization title - Add ThemeManager for theme color management - Improve menu item highlighting and focus handling

This commit is contained in:
cmclark00 2025-03-31 16:02:30 -04:00
parent a8f095cf42
commit 71a8efff91
6 changed files with 555 additions and 114 deletions

View file

@ -105,6 +105,10 @@ class MainActivity : AppCompatActivity(),
// Track currently selected menu item in pause menu for gamepad navigation
private var currentMenuSelection = 0
private val pauseMenuItems = mutableListOf<View>()
private val customizationMenuItems = mutableListOf<View>()
// Add these new properties at the class level
private var currentCustomizationMenuSelection = 0
override fun onCreate(savedInstanceState: Bundle?) {
// Register activity result launcher for high score entry
@ -130,8 +134,8 @@ class MainActivity : AppCompatActivity(),
highScoreManager = HighScoreManager(this)
statsManager = StatsManager(this)
progressionManager = PlayerProgressionManager(this)
themeSelector = binding.themeSelector
blockSkinSelector = binding.blockSkinSelector
themeSelector = binding.customizationThemeSelector!!
blockSkinSelector = binding.customizationBlockSkinSelector!!
pauseMenuScrollView = binding.pauseMenuScrollView
// Initialize gamepad controller
@ -190,44 +194,6 @@ class MainActivity : AppCompatActivity(),
}
}
// Set up landscape mode theme selector if available
val inPauseThemeSelector = findViewById<ThemeSelector>(R.id.inPauseThemeSelector)
inPauseThemeSelector?.onThemeSelected = { themeId: String ->
// Apply the new theme
applyTheme(themeId)
// Provide haptic feedback
gameHaptics.vibrateForPieceLock()
// Refresh the pause menu
showPauseMenu()
}
// Set up block skin selector
blockSkinSelector.onBlockSkinSelected = { skinId: String ->
// Apply the new block skin
gameView.setBlockSkin(skinId)
// Save the selection
progressionManager.setSelectedBlockSkin(skinId)
// Provide haptic feedback
gameHaptics.vibrateForPieceLock()
}
// Set up landscape mode block skin selector if available
val inPauseBlockSkinSelector = findViewById<BlockSkinSelector>(R.id.inPauseBlockSkinSelector)
inPauseBlockSkinSelector?.onBlockSkinSelected = { skinId: String ->
// Apply the new block skin
gameView.setBlockSkin(skinId)
// Save the selection
progressionManager.setSelectedBlockSkin(skinId)
// Provide haptic feedback
gameHaptics.vibrateForPieceLock()
}
// Set up title screen
titleScreen.onStartGame = {
titleScreen.visibility = View.GONE
@ -405,6 +371,18 @@ class MainActivity : AppCompatActivity(),
startActivity(intent)
}
// Set up customization button
binding.customizationButton.setOnClickListener {
gameHaptics.performHapticFeedback(it, HapticFeedbackConstants.VIRTUAL_KEY)
showCustomizationMenu()
}
// Set up customization back button
binding.customizationBackButton.setOnClickListener {
gameHaptics.performHapticFeedback(it, HapticFeedbackConstants.VIRTUAL_KEY)
hideCustomizationMenu()
}
// Initialize level selector
updateLevelSelector()
@ -493,12 +471,6 @@ class MainActivity : AppCompatActivity(),
Log.d("MainActivity", "Triggering game over animation")
gameView.startGameOverAnimation()
// Hide game UI elements in landscape mode
if (resources.configuration.orientation == android.content.res.Configuration.ORIENTATION_LANDSCAPE) {
binding.leftControlsPanel?.visibility = View.GONE
binding.rightControlsPanel?.visibility = View.GONE
}
// Wait a moment before showing progression screen to let animation be visible
Handler(Looper.getMainLooper()).postDelayed({
// Show progression screen first with XP animation
@ -549,11 +521,119 @@ class MainActivity : AppCompatActivity(),
progressionScreen.visibility = View.GONE
}
/**
* Show customization menu
*/
private fun showCustomizationMenu() {
binding.customizationContainer.visibility = View.VISIBLE
binding.pauseContainer.visibility = View.GONE
// Update theme colors
val themeColors = ThemeManager.getThemeColors()
binding.customizationContainer.setBackgroundColor(themeColors.background)
// Update level badge
binding.customizationLevelBadge.setLevel(progressionManager.getPlayerLevel())
binding.customizationLevelBadge.setThemeColor(themeColors.accent)
// Apply theme colors to text
binding.customizationTitle.setTextColor(themeColors.text.toInt())
// Update theme and block skin selectors
binding.customizationThemeSelector.updateThemes(
progressionManager.getUnlockedThemes(),
currentTheme
)
// Set up block skin selector callback
binding.customizationBlockSkinSelector.onBlockSkinSelected = { selectedSkin ->
// Update the game view with the selected block skin
gameView.setBlockSkin(selectedSkin)
// Save the selection
progressionManager.setSelectedBlockSkin(selectedSkin)
}
// Update block skin selector with current selection
binding.customizationBlockSkinSelector.updateBlockSkins(
progressionManager.getUnlockedBlocks(),
gameView.getCurrentBlockSkin(),
progressionManager.getPlayerLevel()
)
// Reset scroll position
binding.customizationMenuScrollView.scrollTo(0, 0)
// Initialize customization menu items
customizationMenuItems.clear()
customizationMenuItems.addAll(listOf(
binding.customizationThemeSelector,
binding.customizationBlockSkinSelector,
binding.customizationBackButton
).filterNotNull().filter { it.visibility == View.VISIBLE })
// Set initial selection and highlight
currentCustomizationMenuSelection = 0
highlightCustomizationMenuItem(currentCustomizationMenuSelection)
// Set initial focus to theme selector
binding.customizationThemeSelector.requestFocus()
// Initialize customization menu navigation
initCustomizationMenuNavigation()
}
private fun initCustomizationMenuNavigation() {
customizationMenuItems.clear()
// Add items in order
customizationMenuItems.addAll(listOf(
binding.customizationThemeSelector,
binding.customizationBlockSkinSelector,
binding.customizationBackButton
).filterNotNull().filter { it.visibility == View.VISIBLE })
// Set up focus change listener for scrolling
binding.customizationMenuScrollView.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
// When scroll view gets focus, scroll to focused item
val focusedView = currentFocus
if (focusedView != null) {
val scrollView = binding.customizationMenuScrollView
val itemTop = focusedView.top
val scrollViewHeight = scrollView.height
// Calculate scroll position to center the focused item
val scrollY = itemTop - (scrollViewHeight / 2) + (focusedView.height / 2)
scrollView.smoothScrollTo(0, scrollY)
}
}
}
// Set up focus handling between items
customizationMenuItems.forEachIndexed { index, item ->
item.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
currentCustomizationMenuSelection = index
highlightCustomizationMenuItem(index)
}
}
}
}
/**
* Hide customization menu
*/
private fun hideCustomizationMenu() {
binding.customizationContainer.visibility = View.GONE
binding.pauseContainer.visibility = View.VISIBLE
}
/**
* Show settings menu
*/
private fun showPauseMenu() {
binding.pauseContainer.visibility = View.VISIBLE
binding.customizationContainer.visibility = View.GONE
// Set button visibility based on game state
if (gameView.isPaused) {
@ -589,6 +669,7 @@ class MainActivity : AppCompatActivity(),
binding.pauseLevelDownButton?.setTextColor(themeColor) // Safe call
binding.settingsButton?.setTextColor(themeColor) // Safe call for sound toggle button text
binding.musicToggle?.setColorFilter(themeColor) // Safe call
binding.customizationButton?.setTextColor(themeColor) // Safe call
// Apply theme colors to text elements (using safe calls)
binding.settingsTitle?.setTextColor(themeColor)
@ -596,30 +677,8 @@ class MainActivity : AppCompatActivity(),
binding.musicText?.setTextColor(themeColor)
binding.pauseLevelText?.setTextColor(themeColor)
// Apply theme to specific labels in portrait/landscape - RELY ON HELPER BELOW
if (isLandscape) {
// findViewById<TextView>(R.id.selectThemeTextLandscape)?.setTextColor(themeColor) // REMOVED
// findViewById<TextView>(R.id.selectBlockSkinTextLandscape)?.setTextColor(themeColor) // REMOVED
// Also color general text views in the landscape container, avoiding selectors
applyThemeColorToTextViews(findViewById(R.id.pauseContainer), themeColor)
} else {
// binding.selectThemeText?.setTextColor(themeColor) // REMOVED
// binding.selectBlockSkinText?.setTextColor(themeColor) // REMOVED
// Apply to portrait container as well if needed (assuming root or specific container)
applyThemeColorToTextViews(binding.pauseContainer, themeColor) // Apply to main container
}
// Update theme selector instances
val currentUnlockedThemes = progressionManager.getUnlockedThemes()
binding.themeSelector.updateThemes(currentUnlockedThemes, currentTheme)
findViewById<ThemeSelector>(R.id.inPauseThemeSelector)?.updateThemes(currentUnlockedThemes, currentTheme)
// Update block skin selector instances
val currentBlockSkin = gameView.getCurrentBlockSkin()
val currentUnlockedBlocks = progressionManager.getUnlockedBlocks()
val currentLevel = progressionManager.getPlayerLevel()
binding.blockSkinSelector.updateBlockSkins(currentUnlockedBlocks, currentBlockSkin, currentLevel)
findViewById<BlockSkinSelector>(R.id.inPauseBlockSkinSelector)?.updateBlockSkins(currentUnlockedBlocks, currentBlockSkin, currentLevel)
// Apply to portrait container as well if needed (assuming root or specific container)
applyThemeColorToTextViews(binding.pauseContainer, themeColor) // Apply to main container
// Reset scroll position
binding.pauseMenuScrollView?.scrollTo(0, 0)
@ -913,7 +972,7 @@ class MainActivity : AppCompatActivity(),
* Update the theme selector with unlocked themes
*/
private fun updateThemeSelector() {
binding.themeSelector.updateThemes(
binding.customizationThemeSelector?.updateThemes(
unlockedThemes = progressionManager.getUnlockedThemes(),
currentTheme = currentTheme
)
@ -1224,8 +1283,10 @@ class MainActivity : AppCompatActivity(),
runOnUiThread {
if (binding.pauseContainer.visibility == View.VISIBLE) {
moveMenuSelectionUp()
// Ensure the selected item is visible
scrollToSelectedItem()
} else if (binding.customizationContainer.visibility == View.VISIBLE) {
moveCustomizationMenuSelectionUp()
scrollToSelectedCustomizationItem()
}
}
}
@ -1234,8 +1295,10 @@ class MainActivity : AppCompatActivity(),
runOnUiThread {
if (binding.pauseContainer.visibility == View.VISIBLE) {
moveMenuSelectionDown()
// Ensure the selected item is visible
scrollToSelectedItem()
} else if (binding.customizationContainer.visibility == View.VISIBLE) {
moveCustomizationMenuSelectionDown()
scrollToSelectedCustomizationItem()
}
}
}
@ -1248,18 +1311,30 @@ class MainActivity : AppCompatActivity(),
is ThemeSelector -> selectedItem.focusPreviousItem()
is BlockSkinSelector -> selectedItem.focusPreviousItem()
}
} else if (binding.customizationContainer.visibility == View.VISIBLE && customizationMenuItems.isNotEmpty()) {
val selectedItem = customizationMenuItems.getOrNull(currentCustomizationMenuSelection)
when (selectedItem) {
is ThemeSelector -> selectedItem.focusPreviousItem()
is BlockSkinSelector -> selectedItem.focusPreviousItem()
}
}
}
}
override fun onMenuRight() {
runOnUiThread {
if (binding.pauseContainer.visibility == View.VISIBLE && pauseMenuItems.isNotEmpty()) {
if (binding.pauseContainer.visibility == View.VISIBLE && pauseMenuItems.isNotEmpty()) {
val selectedItem = pauseMenuItems.getOrNull(currentMenuSelection)
when (selectedItem) {
is ThemeSelector -> selectedItem.focusNextItem()
is BlockSkinSelector -> selectedItem.focusNextItem()
}
} else if (binding.customizationContainer.visibility == View.VISIBLE && customizationMenuItems.isNotEmpty()) {
val selectedItem = customizationMenuItems.getOrNull(currentCustomizationMenuSelection)
when (selectedItem) {
is ThemeSelector -> selectedItem.focusNextItem()
is BlockSkinSelector -> selectedItem.focusNextItem()
}
}
}
}
@ -1267,7 +1342,9 @@ class MainActivity : AppCompatActivity(),
override fun onMenuSelect() {
runOnUiThread {
if (binding.pauseContainer.visibility == View.VISIBLE && pauseMenuItems.isNotEmpty()) {
activateSelectedMenuItem() // Handles selectors and other items
activateSelectedMenuItem()
} else if (binding.customizationContainer.visibility == View.VISIBLE && customizationMenuItems.isNotEmpty()) {
activateSelectedCustomizationMenuItem()
} else if (titleScreen.visibility == View.VISIBLE) {
titleScreen.performClick()
} else if (progressionScreen.visibility == View.VISIBLE) {
@ -1295,12 +1372,9 @@ class MainActivity : AppCompatActivity(),
orderedViews.add(binding.resumeButton)
}
if (binding.pauseStartButton.visibility == View.VISIBLE) {
// In pause menu, the 'Start New Game' button might be visible if paused from title?
// Let's assume pauseRestartButton is the primary one during active pause.
// Keep the logic simple: add if visible.
orderedViews.add(binding.pauseStartButton)
}
orderedViews.add(binding.pauseRestartButton) // Always add restart
orderedViews.add(binding.pauseRestartButton)
// Group 2: Stats and Scoring
orderedViews.add(binding.highScoresButton)
@ -1314,24 +1388,11 @@ class MainActivity : AppCompatActivity(),
orderedViews.add(binding.settingsButton) // Sound toggle (Button)
orderedViews.add(binding.musicToggle) // Music toggle (ImageButton)
// Group 5: Visual customization (Theme and Block Skins)
// Choose the correct instance based on orientation and visibility
val themeSelectorInstance = if (isLandscape) findViewById<ThemeSelector>(R.id.inPauseThemeSelector) else binding.themeSelector
val blockSkinSelectorInstance = if (isLandscape) findViewById<BlockSkinSelector>(R.id.inPauseBlockSkinSelector) else binding.blockSkinSelector
// Only add the selectors if they are actually visible in the current layout
if (themeSelectorInstance?.visibility == View.VISIBLE) {
orderedViews.add(themeSelectorInstance)
}
if (blockSkinSelectorInstance?.visibility == View.VISIBLE) {
orderedViews.add(blockSkinSelectorInstance)
}
// Group 5: Customization Menu
orderedViews.add(binding.customizationButton) // Add the customization button
// Add all non-null, visible items from the defined order to the final navigation list
pauseMenuItems.addAll(orderedViews.filterNotNull().filter { it.visibility == View.VISIBLE })
// Reset selection index (will be set and highlighted in showPauseMenu)
// currentMenuSelection = 0
}
/**
@ -1609,4 +1670,182 @@ class MainActivity : AppCompatActivity(),
// If no landscape ScrollView is found, fall back to the default one
return binding.pauseMenuScrollView
}
// Add these new methods for customization menu navigation
private fun moveCustomizationMenuSelectionUp() {
if (customizationMenuItems.isEmpty()) return
val previousSelection = currentCustomizationMenuSelection
currentCustomizationMenuSelection = (currentCustomizationMenuSelection - 1)
if (currentCustomizationMenuSelection < 0) {
currentCustomizationMenuSelection = 0
return
}
if (previousSelection != currentCustomizationMenuSelection) {
highlightCustomizationMenuItem(currentCustomizationMenuSelection)
}
}
private fun moveCustomizationMenuSelectionDown() {
if (customizationMenuItems.isEmpty()) return
val previousSelection = currentCustomizationMenuSelection
currentCustomizationMenuSelection = (currentCustomizationMenuSelection + 1)
if (currentCustomizationMenuSelection >= customizationMenuItems.size) {
currentCustomizationMenuSelection = customizationMenuItems.size - 1
return
}
if (previousSelection != currentCustomizationMenuSelection) {
highlightCustomizationMenuItem(currentCustomizationMenuSelection)
}
}
private fun highlightCustomizationMenuItem(index: Int) {
if (customizationMenuItems.isEmpty() || index < 0 || index >= customizationMenuItems.size) {
Log.w(TAG, "highlightCustomizationMenuItem called with invalid index: $index or empty items")
return
}
val themeColor = getThemeColor(currentTheme)
val highlightBorderColor = themeColor
customizationMenuItems.forEachIndexed { i, item ->
val isSelected = (i == index)
item.alpha = if (isSelected) 1.0f else 0.7f
if (item is Button || item is ImageButton) {
item.scaleX = 1.0f
item.scaleY = 1.0f
}
when (item) {
is Button -> item.background = ColorDrawable(Color.TRANSPARENT)
is ImageButton -> item.background = null
is ThemeSelector -> {
item.background = ColorDrawable(Color.TRANSPARENT)
item.setHasFocus(false)
}
is BlockSkinSelector -> {
item.background = ColorDrawable(Color.TRANSPARENT)
item.setHasFocus(false)
}
}
if (isSelected) {
if (item is Button || item is ImageButton) {
item.scaleX = 1.1f
item.scaleY = 1.1f
}
val borderWidth = 4
val cornerRadius = 12f
when (item) {
is Button -> {
ContextCompat.getDrawable(this, R.drawable.menu_item_selected)?.mutate()?.let {
if (it is GradientDrawable) {
it.setStroke(borderWidth, highlightBorderColor)
item.background = it
} else {
it.setTint(highlightBorderColor)
item.background = it
}
} ?: run {
item.background = GradientDrawable().apply {
setColor(Color.TRANSPARENT)
setStroke(borderWidth, highlightBorderColor)
setCornerRadius(cornerRadius)
}
}
}
is ImageButton -> {
item.background = GradientDrawable().apply {
setColor(Color.TRANSPARENT)
setStroke(borderWidth, highlightBorderColor)
setCornerRadius(cornerRadius)
}
}
is ThemeSelector -> {
item.background = GradientDrawable().apply {
setColor(Color.TRANSPARENT)
setStroke(borderWidth, highlightBorderColor)
setCornerRadius(cornerRadius)
}
item.setHasFocus(true)
}
is BlockSkinSelector -> {
item.background = GradientDrawable().apply {
setColor(Color.TRANSPARENT)
setStroke(borderWidth, highlightBorderColor)
setCornerRadius(cornerRadius)
}
item.setHasFocus(true)
}
}
}
}
gameHaptics.vibrateForPieceMove()
scrollToSelectedCustomizationItem()
}
private fun scrollToSelectedCustomizationItem() {
if (customizationMenuItems.isEmpty() || currentCustomizationMenuSelection < 0 || currentCustomizationMenuSelection >= customizationMenuItems.size) return
val selectedItem = customizationMenuItems[currentCustomizationMenuSelection]
val scrollView = binding.customizationMenuScrollView
scrollView.post {
try {
val scrollBounds = Rect()
scrollView.getHitRect(scrollBounds)
val itemLocation = IntArray(2)
selectedItem.getLocationOnScreen(itemLocation)
val scrollLocation = IntArray(2)
scrollView.getLocationOnScreen(scrollLocation)
val itemY = itemLocation[1] - scrollLocation[1]
val itemTop = itemY
val itemBottom = itemY + selectedItem.height
val visibleHeight = scrollView.height
val padding = 50
val scrollY = scrollView.scrollY
if (itemTop < padding) {
scrollView.smoothScrollTo(0, scrollY + (itemTop - padding))
} else if (itemBottom > visibleHeight - padding) {
scrollView.smoothScrollTo(0, scrollY + (itemBottom - visibleHeight + padding))
}
} catch (e: Exception) {
Log.e(TAG, "Error scrolling to selected customization item", e)
}
}
}
private fun activateSelectedCustomizationMenuItem() {
if (customizationMenuItems.isEmpty() || currentCustomizationMenuSelection < 0 || currentCustomizationMenuSelection >= customizationMenuItems.size) return
val selectedItem = customizationMenuItems[currentCustomizationMenuSelection]
gameHaptics.vibrateForPieceLock()
when (selectedItem) {
is ThemeSelector -> {
selectedItem.confirmSelection()
Handler(Looper.getMainLooper()).postDelayed({ showCustomizationMenu() }, 100)
}
is BlockSkinSelector -> {
selectedItem.confirmSelection()
// The onBlockSkinSelected callback will handle updating the game view and saving the selection
}
else -> {
selectedItem.performClick()
}
}
}
}

View file

@ -0,0 +1,19 @@
package com.mintris
import android.graphics.Color
object ThemeManager {
data class ThemeColors(
val background: Int,
val text: Int,
val accent: Int
)
fun getThemeColors(): ThemeColors {
return ThemeColors(
background = Color.BLACK,
text = Color.WHITE,
accent = Color.WHITE
)
}
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#80000000" />
<corners android:radius="16dp" />
</shape>

View file

@ -421,22 +421,23 @@
android:src="@drawable/ic_volume_up"
android:contentDescription="@string/music" />
</LinearLayout>
<!-- Theme Selector -->
<com.mintris.ui.ThemeSelector
android:id="@+id/inPauseThemeSelector"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="16dp" />
<!-- Block Skin Selector -->
<com.mintris.ui.BlockSkinSelector
android:id="@+id/inPauseBlockSkinSelector"
android:layout_width="match_parent"
<!-- Customization Button -->
<Button
android:id="@+id/customizationButton"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp" />
android:background="@color/transparent"
android:text="@string/customization"
android:textColor="@color/white"
android:textSize="20sp"
android:textStyle="bold"
android:fontFamily="sans-serif"
android:textAllCaps="false"
android:singleLine="true"
android:focusable="true"
android:focusableInTouchMode="true" />
</LinearLayout>
</ScrollView>
</LinearLayout>
@ -623,4 +624,97 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
</LinearLayout>
<!-- Customization Menu overlay -->
<LinearLayout
android:id="@+id/customizationContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:orientation="vertical"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginTop="32dp"
android:layout_marginBottom="32dp">
<TextView
android:id="@+id/customizationTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/customization"
android:textColor="@color/white"
android:textSize="20sp"
android:textStyle="bold"
android:fontFamily="sans-serif"
android:layout_marginEnd="16dp"
android:textAllCaps="false"
android:singleLine="true" />
<com.mintris.ui.LevelBadge
android:id="@+id/customizationLevelBadge"
android:layout_width="48dp"
android:layout_height="48dp" />
</LinearLayout>
<ScrollView
android:id="@+id/customizationMenuScrollView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scrollbars="none"
android:overScrollMode="never"
android:fillViewport="true"
android:paddingBottom="32dp"
android:focusable="true"
android:focusableInTouchMode="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:paddingTop="16dp"
android:paddingBottom="32dp">
<!-- Theme Selector -->
<com.mintris.ui.ThemeSelector
android:id="@+id/customizationThemeSelector"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:focusable="true"
android:focusableInTouchMode="true" />
<!-- Block Skin Selector -->
<com.mintris.ui.BlockSkinSelector
android:id="@+id/customizationBlockSkinSelector"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:focusableInTouchMode="true" />
<!-- Back Button -->
<Button
android:id="@+id/customizationBackButton"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:background="@color/transparent"
android:text="@string/back"
android:textColor="@color/white"
android:textSize="24sp"
android:textStyle="bold"
android:fontFamily="sans-serif"
android:textAllCaps="false"
android:focusable="true"
android:focusableInTouchMode="true" />
</LinearLayout>
</ScrollView>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -518,22 +518,104 @@
android:src="@drawable/ic_volume_up"
android:contentDescription="@string/music" />
</LinearLayout>
<!-- Customization Button -->
<Button
android:id="@+id/customizationButton"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@color/transparent"
android:text="@string/customization"
android:textColor="@color/white"
android:textSize="24sp"
android:textStyle="bold"
android:fontFamily="sans-serif"
android:textAllCaps="false" />
</LinearLayout>
</ScrollView>
</LinearLayout>
<!-- Customization Menu overlay -->
<LinearLayout
android:id="@+id/customizationContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:orientation="vertical"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginTop="32dp"
android:layout_marginBottom="32dp">
<TextView
android:id="@+id/customizationTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/customization"
android:textColor="@color/white"
android:textSize="20sp"
android:textStyle="bold"
android:fontFamily="sans-serif"
android:layout_marginEnd="16dp"
android:textAllCaps="false"
android:singleLine="true" />
<com.mintris.ui.LevelBadge
android:id="@+id/customizationLevelBadge"
android:layout_width="48dp"
android:layout_height="48dp" />
</LinearLayout>
<ScrollView
android:id="@+id/customizationMenuScrollView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scrollbars="none"
android:overScrollMode="never"
android:fillViewport="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:paddingTop="16dp"
android:paddingBottom="32dp">
<!-- Theme Selector -->
<com.mintris.ui.ThemeSelector
android:id="@+id/themeSelector"
android:id="@+id/customizationThemeSelector"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="16dp" />
<!-- Block Skin Selector -->
<com.mintris.ui.BlockSkinSelector
android:id="@+id/blockSkinSelector"
android:id="@+id/customizationBlockSkinSelector"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- Back Button -->
<Button
android:id="@+id/customizationBackButton"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp" />
android:layout_marginTop="32dp"
android:background="@color/transparent"
android:text="@string/back"
android:textColor="@color/white"
android:textSize="24sp"
android:textStyle="bold"
android:fontFamily="sans-serif"
android:textAllCaps="false" />
</LinearLayout>
</ScrollView>
</LinearLayout>

View file

@ -47,4 +47,5 @@
<string name="tetrises">tetrises: %d</string>
<string name="reset_stats">reset stats</string>
<string name="music">music</string>
<string name="customization">Customization</string>
</resources>