From 6044b0d93ba801e7594ad37ca978d91198b80156 Mon Sep 17 00:00:00 2001 From: cmclark00 Date: Tue, 1 Apr 2025 11:47:10 -0400 Subject: [PATCH] Fix: Adjust layout padding to respect display cutouts --- README.md | 145 ++++++++---------- .../com/pixelmintdrop/HighScoresActivity.kt | 19 +++ .../java/com/pixelmintdrop/MainActivity.kt | 63 ++++++++ .../java/com/pixelmintdrop/StatsActivity.kt | 21 ++- .../main/res/layout-land/activity_stats.xml | 3 +- app/src/main/res/layout-land/high_scores.xml | 3 +- app/src/main/res/layout/activity_stats.xml | 3 +- app/src/main/res/layout/high_scores.xml | 3 +- 8 files changed, 176 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index d9af2c4..eb42044 100644 --- a/README.md +++ b/README.md @@ -1,122 +1,109 @@ # 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 ### Core Gameplay -- Classic Tetris mechanics +- Familiar block-stacking gameplay - 7-bag randomizer for piece distribution -- Ghost piece preview -- Hard drop and soft drop -- T-Spin detection and scoring -- High score system with top 5 scores +- Ghost piece preview for precise placement +- Hard drop and soft drop controls +- Advanced move detection (e.g., T-Spins) and scoring +- Persistent high score system (Top 5) ### Modern Android Features - Optimized for Android 11+ (API 30+) -- Hardware-accelerated rendering -- High refresh rate support -- Haptic feedback for piece movement and line clears -- Dark theme support -- Responsive touch controls -- Edge-to-edge display support +- Smooth hardware-accelerated rendering +- High refresh rate support for fluid visuals +- Haptic feedback for tactile interaction (piece movement, line clears) +- Automatic Dark theme support +- Intuitive and responsive touch controls +- Full edge-to-edge display utilization ### Scoring System -The game features a comprehensive scoring system: +Pixelmint Drop features a comprehensive scoring system designed to reward skillful play: #### Base Scores -- Single line: 100 points -- Double: 300 points -- Triple: 500 points -- Quad (4 lines): 1200 points +- Single line clear: 100 points +- Double line clear: 300 points +- Triple line clear: 500 points +- Quad line clear (4 lines): 1200 points -#### Multipliers +#### Multipliers & Bonuses -1. **Level Multiplier** - - Score is multiplied by current level - - Level increases every 10 lines cleared - - Maximum level is 20 +1. **Level Multiplier** + - Score multiplied by the current level. + - Level increases every 10 lines cleared (Max Level: 20). -2. **Combo System** - - Combo counter increases with each line clear - - Resets if no lines are cleared - - Multipliers: - - 1 combo: 1.0x - - 2 combos: 1.5x - - 3 combos: 2.0x - - 4 combos: 2.5x - - 5+ combos: 3.0x +2. **Combo System** + - Consecutive line clears increase a combo multiplier. + - Resets if a piece is placed without clearing lines. + - Multipliers: 1x (1), 1.5x (2), 2.0x (3), 2.5x (4), 3.0x (5+ combos). -3. **Back-to-Back Quad** - - 50% bonus (1.5x) for consecutive Quad clears - - Resets if a non-Quad clear is performed +3. **Back-to-Back Quad Bonus** + - 50% bonus (1.5x) for clearing 4 lines consecutively. + - Resets on non-Quad clears. -4. **Perfect Clear** - - 2x for single line - - 3x for double - - 4x for triple - - 5x for Quad - - Awarded when clearing lines without leaving blocks +4. **Perfect Clear Bonus** + - Bonus multiplier for clearing lines leaving an empty board. + - Multipliers: 2x (Single), 3x (Double), 4x (Triple), 5x (Quad). -5. **All Clear** - - 2x multiplier when clearing all blocks - - Requires no blocks in grid and no pieces in play +5. **All Clear Bonus** + - 2x multiplier for clearing the entire grid of blocks. -6. **T-Spin Bonuses** - - Single: 2x - - Double: 4x - - Triple: 6x +6. **T-Spin Bonuses** + - Additional multipliers for executing T-Spin maneuvers. + - Single: 2x, Double: 4x, Triple: 6x. ### Controls -- Swipe left/right to move piece -- Swipe down quickly for hard drop -- Swipe down slowly for soft drop -- Single tap to rotate +- **Swipe left/right:** Move piece horizontally. +- **Swipe down quickly:** Hard drop (instant placement). +- **Swipe down slowly:** Soft drop (faster downward movement). +- **Single tap:** Rotate piece. ### Visual Effects -- Smooth piece movement animations -- Pulsing border glow during line clears -- Ghost piece preview -- Block glow effects -- Subtle grid lines -- Animated title screen with falling pieces +- Silky-smooth piece movement animations. +- Dynamic pulsing border glow during line clears. +- Clear ghost piece preview for accurate drops. +- Subtle block glow effects. +- Clean grid lines for better visibility. +- Engaging animated title screen featuring falling pieces. ## Technical Details ### Requirements -- Android 11 (API 30) or higher -- Hardware acceleration support -- 2GB RAM minimum +- Android 11 (API 30) or higher. +- Hardware acceleration support. +- Minimum 2GB RAM recommended. ### Performance Optimizations -- Hardware-accelerated rendering -- Efficient collision detection -- Optimized grid operations -- Smooth animations at 60fps -- Background thread for score calculations +- Leverages hardware-accelerated rendering via Android Canvas. +- Efficient collision detection algorithms. +- Optimized grid data structures and operations. +- Targets smooth 60fps animations. +- Score calculations performed on a background thread to maintain UI responsiveness. ### Architecture -- Written in Kotlin -- Uses Android Canvas for rendering -- Follows Material Design guidelines -- Implements high score persistence using SharedPreferences +- Developed entirely in Kotlin. +- Utilizes Android Canvas API for custom drawing. +- Adheres to Material Design principles for UI/UX. +- Persists high scores using SharedPreferences. ## Building from Source -1. Clone the repository: -```bash -git clone https://github.com/cmclark00/pixelmintdrop.git -``` - -2. Open the project in Android Studio - -3. Build and run on your device or emulator +1. Clone the repository: + ```bash + git clone https://github.com/cmclark00/pixelmintdrop.git + ``` +2. Open the project in Android Studio. +3. Build and run on a compatible Android device or emulator (API 30+). ## 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 -This project is licensed under the MIT License - see the LICENSE file for details. \ No newline at end of file +This project is licensed under the MIT License - see the `LICENSE` file for details. \ No newline at end of file diff --git a/app/src/main/java/com/pixelmintdrop/HighScoresActivity.kt b/app/src/main/java/com/pixelmintdrop/HighScoresActivity.kt index ea21392..40cda7d 100644 --- a/app/src/main/java/com/pixelmintdrop/HighScoresActivity.kt +++ b/app/src/main/java/com/pixelmintdrop/HighScoresActivity.kt @@ -11,6 +11,11 @@ import android.graphics.Color import android.util.Log import android.view.KeyEvent 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() { private lateinit var binding: HighScoresBinding @@ -26,6 +31,20 @@ class HighScoresActivity : AppCompatActivity() { binding = HighScoresBinding.inflate(layoutInflater) 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) progressionManager = PlayerProgressionManager(this) diff --git a/app/src/main/java/com/pixelmintdrop/MainActivity.kt b/app/src/main/java/com/pixelmintdrop/MainActivity.kt index f5e39e8..2aa50c7 100644 --- a/app/src/main/java/com/pixelmintdrop/MainActivity.kt +++ b/app/src/main/java/com/pixelmintdrop/MainActivity.kt @@ -45,6 +45,10 @@ import android.widget.ImageButton import android.graphics.drawable.GradientDrawable import android.widget.TextView 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(), GamepadController.GamepadConnectionListener, @@ -135,6 +139,65 @@ class MainActivity : AppCompatActivity(), binding = ActivityMainBinding.inflate(layoutInflater) 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 disableAndroidBackGesture() diff --git a/app/src/main/java/com/pixelmintdrop/StatsActivity.kt b/app/src/main/java/com/pixelmintdrop/StatsActivity.kt index 1652365..eadd263 100644 --- a/app/src/main/java/com/pixelmintdrop/StatsActivity.kt +++ b/app/src/main/java/com/pixelmintdrop/StatsActivity.kt @@ -1,12 +1,17 @@ package com.pixelmintdrop +import android.graphics.Color +import android.graphics.Rect import android.os.Bundle +import android.view.View import androidx.appcompat.app.AlertDialog 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.model.StatsManager import com.pixelmintdrop.model.PlayerProgressionManager -import android.graphics.Color import java.text.SimpleDateFormat import java.util.* @@ -21,6 +26,20 @@ class StatsActivity : AppCompatActivity() { binding = ActivityStatsBinding.inflate(layoutInflater) 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) progressionManager = PlayerProgressionManager(this) diff --git a/app/src/main/res/layout-land/activity_stats.xml b/app/src/main/res/layout-land/activity_stats.xml index 5570cf4..6d790c1 100644 --- a/app/src/main/res/layout-land/activity_stats.xml +++ b/app/src/main/res/layout-land/activity_stats.xml @@ -3,7 +3,8 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/black" - android:orientation="vertical"> + android:orientation="vertical" + android:fitsSystemWindows="true"> + android:padding="16dp" + android:fitsSystemWindows="true"> + android:background="@color/black" + android:fitsSystemWindows="true"> + android:padding="16dp" + android:fitsSystemWindows="true">