Fix: Adjust layout padding to respect display cutouts

This commit is contained in:
cmclark00 2025-04-01 11:47:10 -04:00
parent 501e5b37fc
commit 6044b0d93b
8 changed files with 176 additions and 84 deletions

145
README.md
View file

@ -1,122 +1,109 @@
# pixelmintdrop # pixelmintdrop
A modern Tetris implementation for Android, featuring smooth animations, responsive controls, and a beautiful minimalist design. A modern falling block puzzle game for Android, featuring smooth animations, responsive controls, and a beautiful minimalist design.
## Features ## Features
### Core Gameplay ### Core Gameplay
- Classic Tetris mechanics - Familiar block-stacking gameplay
- 7-bag randomizer for piece distribution - 7-bag randomizer for piece distribution
- Ghost piece preview - Ghost piece preview for precise placement
- Hard drop and soft drop - Hard drop and soft drop controls
- T-Spin detection and scoring - Advanced move detection (e.g., T-Spins) and scoring
- High score system with top 5 scores - Persistent high score system (Top 5)
### Modern Android Features ### Modern Android Features
- Optimized for Android 11+ (API 30+) - Optimized for Android 11+ (API 30+)
- Hardware-accelerated rendering - Smooth hardware-accelerated rendering
- High refresh rate support - High refresh rate support for fluid visuals
- Haptic feedback for piece movement and line clears - Haptic feedback for tactile interaction (piece movement, line clears)
- Dark theme support - Automatic Dark theme support
- Responsive touch controls - Intuitive and responsive touch controls
- Edge-to-edge display support - Full edge-to-edge display utilization
### Scoring System ### Scoring System
The game features a comprehensive scoring system: Pixelmint Drop features a comprehensive scoring system designed to reward skillful play:
#### Base Scores #### Base Scores
- Single line: 100 points - Single line clear: 100 points
- Double: 300 points - Double line clear: 300 points
- Triple: 500 points - Triple line clear: 500 points
- Quad (4 lines): 1200 points - Quad line clear (4 lines): 1200 points
#### Multipliers #### Multipliers & Bonuses
1. **Level Multiplier** 1. **Level Multiplier**
- Score is multiplied by current level - Score multiplied by the current level.
- Level increases every 10 lines cleared - Level increases every 10 lines cleared (Max Level: 20).
- Maximum level is 20
2. **Combo System** 2. **Combo System**
- Combo counter increases with each line clear - Consecutive line clears increase a combo multiplier.
- Resets if no lines are cleared - Resets if a piece is placed without clearing lines.
- Multipliers: - Multipliers: 1x (1), 1.5x (2), 2.0x (3), 2.5x (4), 3.0x (5+ combos).
- 1 combo: 1.0x
- 2 combos: 1.5x
- 3 combos: 2.0x
- 4 combos: 2.5x
- 5+ combos: 3.0x
3. **Back-to-Back Quad** 3. **Back-to-Back Quad Bonus**
- 50% bonus (1.5x) for consecutive Quad clears - 50% bonus (1.5x) for clearing 4 lines consecutively.
- Resets if a non-Quad clear is performed - Resets on non-Quad clears.
4. **Perfect Clear** 4. **Perfect Clear Bonus**
- 2x for single line - Bonus multiplier for clearing lines leaving an empty board.
- 3x for double - Multipliers: 2x (Single), 3x (Double), 4x (Triple), 5x (Quad).
- 4x for triple
- 5x for Quad
- Awarded when clearing lines without leaving blocks
5. **All Clear** 5. **All Clear Bonus**
- 2x multiplier when clearing all blocks - 2x multiplier for clearing the entire grid of blocks.
- Requires no blocks in grid and no pieces in play
6. **T-Spin Bonuses** 6. **T-Spin Bonuses**
- Single: 2x - Additional multipliers for executing T-Spin maneuvers.
- Double: 4x - Single: 2x, Double: 4x, Triple: 6x.
- Triple: 6x
### Controls ### Controls
- Swipe left/right to move piece - **Swipe left/right:** Move piece horizontally.
- Swipe down quickly for hard drop - **Swipe down quickly:** Hard drop (instant placement).
- Swipe down slowly for soft drop - **Swipe down slowly:** Soft drop (faster downward movement).
- Single tap to rotate - **Single tap:** Rotate piece.
### Visual Effects ### Visual Effects
- Smooth piece movement animations - Silky-smooth piece movement animations.
- Pulsing border glow during line clears - Dynamic pulsing border glow during line clears.
- Ghost piece preview - Clear ghost piece preview for accurate drops.
- Block glow effects - Subtle block glow effects.
- Subtle grid lines - Clean grid lines for better visibility.
- Animated title screen with falling pieces - Engaging animated title screen featuring falling pieces.
## Technical Details ## Technical Details
### Requirements ### Requirements
- Android 11 (API 30) or higher - Android 11 (API 30) or higher.
- Hardware acceleration support - Hardware acceleration support.
- 2GB RAM minimum - Minimum 2GB RAM recommended.
### Performance Optimizations ### Performance Optimizations
- Hardware-accelerated rendering - Leverages hardware-accelerated rendering via Android Canvas.
- Efficient collision detection - Efficient collision detection algorithms.
- Optimized grid operations - Optimized grid data structures and operations.
- Smooth animations at 60fps - Targets smooth 60fps animations.
- Background thread for score calculations - Score calculations performed on a background thread to maintain UI responsiveness.
### Architecture ### Architecture
- Written in Kotlin - Developed entirely in Kotlin.
- Uses Android Canvas for rendering - Utilizes Android Canvas API for custom drawing.
- Follows Material Design guidelines - Adheres to Material Design principles for UI/UX.
- Implements high score persistence using SharedPreferences - Persists high scores using SharedPreferences.
## Building from Source ## Building from Source
1. Clone the repository: 1. Clone the repository:
```bash ```bash
git clone https://github.com/cmclark00/pixelmintdrop.git git clone https://github.com/cmclark00/pixelmintdrop.git
``` ```
2. Open the project in Android Studio.
2. Open the project in Android Studio 3. Build and run on a compatible Android device or emulator (API 30+).
3. Build and run on your device or emulator
## Contributing ## Contributing
Contributions are welcome! Please feel free to submit a Pull Request. Contributions are welcome! Please feel free to submit a Pull Request with improvements or bug fixes.
## License ## License
This project is licensed under the MIT License - see the LICENSE file for details. This project is licensed under the MIT License - see the `LICENSE` file for details.

View file

@ -11,6 +11,11 @@ import android.graphics.Color
import android.util.Log import android.util.Log
import android.view.KeyEvent import android.view.KeyEvent
import android.view.InputDevice import android.view.InputDevice
import android.graphics.Rect
import android.view.View
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
class HighScoresActivity : AppCompatActivity() { class HighScoresActivity : AppCompatActivity() {
private lateinit var binding: HighScoresBinding private lateinit var binding: HighScoresBinding
@ -26,6 +31,20 @@ class HighScoresActivity : AppCompatActivity() {
binding = HighScoresBinding.inflate(layoutInflater) binding = HighScoresBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
// Apply insets to the root view
val initialPadding = Rect(binding.root.paddingLeft, binding.root.paddingTop,
binding.root.paddingRight, binding.root.paddingBottom)
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(
left = initialPadding.left + insets.left,
top = initialPadding.top + insets.top,
right = initialPadding.right + insets.right,
bottom = initialPadding.bottom + insets.bottom
)
WindowInsetsCompat.CONSUMED
}
highScoreManager = HighScoreManager(this) highScoreManager = HighScoreManager(this)
progressionManager = PlayerProgressionManager(this) progressionManager = PlayerProgressionManager(this)

View file

@ -45,6 +45,10 @@ import android.widget.ImageButton
import android.graphics.drawable.GradientDrawable import android.graphics.drawable.GradientDrawable
import android.widget.TextView import android.widget.TextView
import android.widget.Switch import android.widget.Switch
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import android.view.ViewGroup
import androidx.core.view.updatePadding
class MainActivity : AppCompatActivity(), class MainActivity : AppCompatActivity(),
GamepadController.GamepadConnectionListener, GamepadController.GamepadConnectionListener,
@ -135,6 +139,65 @@ class MainActivity : AppCompatActivity(),
binding = ActivityMainBinding.inflate(layoutInflater) binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
// Store initial padding values before applying insets
val initialPausePadding = Rect(binding.pauseContainer.paddingLeft, binding.pauseContainer.paddingTop,
binding.pauseContainer.paddingRight, binding.pauseContainer.paddingBottom)
val initialGameOverPadding = Rect(binding.gameOverContainer.paddingLeft, binding.gameOverContainer.paddingTop,
binding.gameOverContainer.paddingRight, binding.gameOverContainer.paddingBottom)
val initialCustomizationPadding = Rect(binding.customizationContainer.paddingLeft, binding.customizationContainer.paddingTop,
binding.customizationContainer.paddingRight, binding.customizationContainer.paddingBottom)
val initialProgressionPadding = Rect(binding.progressionScreen.paddingLeft, binding.progressionScreen.paddingTop,
binding.progressionScreen.paddingRight, binding.progressionScreen.paddingBottom)
// Apply insets to the pause container
ViewCompat.setOnApplyWindowInsetsListener(binding.pauseContainer) { view, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
// Apply insets by adding to initial padding
view.updatePadding(
left = initialPausePadding.left + insets.left,
top = initialPausePadding.top + insets.top,
right = initialPausePadding.right + insets.right,
bottom = initialPausePadding.bottom + insets.bottom
)
WindowInsetsCompat.CONSUMED
}
// Apply insets to the game over container
ViewCompat.setOnApplyWindowInsetsListener(binding.gameOverContainer) { view, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(
left = initialGameOverPadding.left + insets.left,
top = initialGameOverPadding.top + insets.top,
right = initialGameOverPadding.right + insets.right,
bottom = initialGameOverPadding.bottom + insets.bottom
)
WindowInsetsCompat.CONSUMED
}
// Apply insets to the customization container
ViewCompat.setOnApplyWindowInsetsListener(binding.customizationContainer) { view, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(
left = initialCustomizationPadding.left + insets.left,
top = initialCustomizationPadding.top + insets.top,
right = initialCustomizationPadding.right + insets.right,
bottom = initialCustomizationPadding.bottom + insets.bottom
)
WindowInsetsCompat.CONSUMED
}
// Apply insets to the progression screen
ViewCompat.setOnApplyWindowInsetsListener(binding.progressionScreen) { view, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(
left = initialProgressionPadding.left + insets.left,
top = initialProgressionPadding.top + insets.top,
right = initialProgressionPadding.right + insets.right,
bottom = initialProgressionPadding.bottom + insets.bottom
)
WindowInsetsCompat.CONSUMED
}
// Disable Android back gesture to prevent accidental app exits // Disable Android back gesture to prevent accidental app exits
disableAndroidBackGesture() disableAndroidBackGesture()

View file

@ -1,12 +1,17 @@
package com.pixelmintdrop package com.pixelmintdrop
import android.graphics.Color
import android.graphics.Rect
import android.os.Bundle import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import com.pixelmintdrop.databinding.ActivityStatsBinding import com.pixelmintdrop.databinding.ActivityStatsBinding
import com.pixelmintdrop.model.StatsManager import com.pixelmintdrop.model.StatsManager
import com.pixelmintdrop.model.PlayerProgressionManager import com.pixelmintdrop.model.PlayerProgressionManager
import android.graphics.Color
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -21,6 +26,20 @@ class StatsActivity : AppCompatActivity() {
binding = ActivityStatsBinding.inflate(layoutInflater) binding = ActivityStatsBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
// Apply insets to the root view
val initialPadding = Rect(binding.root.paddingLeft, binding.root.paddingTop,
binding.root.paddingRight, binding.root.paddingBottom)
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(
left = initialPadding.left + insets.left,
top = initialPadding.top + insets.top,
right = initialPadding.right + insets.right,
bottom = initialPadding.bottom + insets.bottom
)
WindowInsetsCompat.CONSUMED
}
statsManager = StatsManager(this) statsManager = StatsManager(this)
progressionManager = PlayerProgressionManager(this) progressionManager = PlayerProgressionManager(this)

View file

@ -3,7 +3,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/black" android:background="@color/black"
android:orientation="vertical"> android:orientation="vertical"
android:fitsSystemWindows="true">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -4,7 +4,8 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/black" android:background="@color/black"
android:orientation="vertical" android:orientation="vertical"
android:padding="16dp"> android:padding="16dp"
android:fitsSystemWindows="true">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -3,7 +3,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/black"> android:background="@color/black"
android:fitsSystemWindows="true">
<ScrollView <ScrollView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -4,7 +4,8 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/black" android:background="@color/black"
android:orientation="vertical" android:orientation="vertical"
android:padding="16dp"> android:padding="16dp"
android:fitsSystemWindows="true">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"