Auto-commit after successful build: update app gameplay/UI, improve build setup
Changed files:\napp/build.gradle.kts app/src/main/java/com/kawomi/githugandroid/GitHugApp.kt
This commit is contained in:
@@ -11,8 +11,8 @@ android {
|
|||||||
applicationId = "com.kawomi.githugandroid"
|
applicationId = "com.kawomi.githugandroid"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 5
|
versionCode = 6
|
||||||
versionName = "0.1.4"
|
versionName = "0.1.5"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
@@ -70,7 +70,6 @@ dependencies {
|
|||||||
implementation("androidx.compose.ui:ui-graphics")
|
implementation("androidx.compose.ui:ui-graphics")
|
||||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||||
implementation("androidx.compose.foundation:foundation")
|
implementation("androidx.compose.foundation:foundation")
|
||||||
implementation("androidx.compose.material:material-icons-extended")
|
|
||||||
implementation("androidx.compose.material3:material3:1.2.1")
|
implementation("androidx.compose.material3:material3:1.2.1")
|
||||||
implementation("com.google.android.material:material:1.12.0")
|
implementation("com.google.android.material:material:1.12.0")
|
||||||
|
|
||||||
|
|||||||
@@ -8,19 +8,12 @@ import androidx.compose.foundation.text.BasicTextField
|
|||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
|
||||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
|
||||||
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
|
||||||
import androidx.compose.material.icons.filled.KeyboardTab
|
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.FilterChip
|
import androidx.compose.material3.FilterChip
|
||||||
import androidx.compose.material3.FilterChipDefaults
|
import androidx.compose.material3.FilterChipDefaults
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
@@ -39,6 +32,8 @@ import androidx.compose.ui.focus.FocusRequester
|
|||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.graphics.SolidColor
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
import androidx.compose.ui.text.TextRange
|
import androidx.compose.ui.text.TextRange
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
@@ -47,6 +42,8 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
private val AppBackground = Color(0xFF000000)
|
private val AppBackground = Color(0xFF000000)
|
||||||
private val PanelPrimary = Color(0xFF121212)
|
private val PanelPrimary = Color(0xFF121212)
|
||||||
@@ -83,6 +80,7 @@ fun GitHugApp() {
|
|||||||
var repo by remember { mutableStateOf(levels.first().setup()) }
|
var repo by remember { mutableStateOf(levels.first().setup()) }
|
||||||
var commandInput by remember { mutableStateOf(TextFieldValue("")) }
|
var commandInput by remember { mutableStateOf(TextFieldValue("")) }
|
||||||
var inputFieldVersion by remember { mutableStateOf(0) }
|
var inputFieldVersion by remember { mutableStateOf(0) }
|
||||||
|
var suppressedImeEcho by remember { mutableStateOf<String?>(null) }
|
||||||
var output by remember { mutableStateOf(listOf("Welcome to GitHug Android.")) }
|
var output by remember { mutableStateOf(listOf("Welcome to GitHug Android.")) }
|
||||||
var hintIndex by remember { mutableStateOf(0) }
|
var hintIndex by remember { mutableStateOf(0) }
|
||||||
var completedLevels by remember { mutableStateOf(setOf<String>()) }
|
var completedLevels by remember { mutableStateOf(setOf<String>()) }
|
||||||
@@ -91,20 +89,21 @@ fun GitHugApp() {
|
|||||||
var historyDraft by remember { mutableStateOf("") }
|
var historyDraft by remember { mutableStateOf("") }
|
||||||
val currentLevel = levels[currentLevelIndex]
|
val currentLevel = levels[currentLevelIndex]
|
||||||
|
|
||||||
val solved = currentLevel.validator(repo)
|
|
||||||
|
|
||||||
LaunchedEffect(currentLevelIndex) {
|
LaunchedEffect(currentLevelIndex) {
|
||||||
screenScrollState.scrollTo(0)
|
screenScrollState.scrollTo(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearCommandInput() {
|
fun clearCommandInput(recreateField: Boolean = false) {
|
||||||
commandInput = TextFieldValue(text = "", selection = TextRange.Zero)
|
commandInput = TextFieldValue(text = "", selection = TextRange.Zero)
|
||||||
|
if (recreateField) {
|
||||||
inputFieldVersion += 1
|
inputFieldVersion += 1
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun resetCurrentLevel(message: String = "Level reset.") {
|
fun resetCurrentLevel(message: String = "Level reset.") {
|
||||||
repo = currentLevel.setup()
|
repo = currentLevel.setup()
|
||||||
clearCommandInput()
|
clearCommandInput()
|
||||||
|
suppressedImeEcho = null
|
||||||
output = listOf(message)
|
output = listOf(message)
|
||||||
hintIndex = 0
|
hintIndex = 0
|
||||||
historyIndex = -1
|
historyIndex = -1
|
||||||
@@ -164,10 +163,12 @@ fun GitHugApp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun runCommand() {
|
fun runCommand() {
|
||||||
val raw = commandInput.text.trim()
|
val submittedText = commandInput.text
|
||||||
|
val raw = submittedText.trim()
|
||||||
if (raw.isBlank()) return
|
if (raw.isBlank()) return
|
||||||
|
|
||||||
clearCommandInput()
|
suppressedImeEcho = submittedText
|
||||||
|
clearCommandInput(recreateField = true)
|
||||||
|
|
||||||
if (commandHistory.lastOrNull() != raw) {
|
if (commandHistory.lastOrNull() != raw) {
|
||||||
commandHistory = commandHistory + raw
|
commandHistory = commandHistory + raw
|
||||||
@@ -196,11 +197,13 @@ fun GitHugApp() {
|
|||||||
repo = nextLevel.setup()
|
repo = nextLevel.setup()
|
||||||
output = listOf("Loaded level: ${nextLevel.title}")
|
output = listOf("Loaded level: ${nextLevel.title}")
|
||||||
clearCommandInput()
|
clearCommandInput()
|
||||||
|
suppressedImeEcho = null
|
||||||
hintIndex = 0
|
hintIndex = 0
|
||||||
} else {
|
} else {
|
||||||
repo = newRepo
|
repo = newRepo
|
||||||
output = listOf("🏁 All available MVP levels completed.")
|
output = listOf("🏁 All available MVP levels completed.")
|
||||||
clearCommandInput()
|
clearCommandInput()
|
||||||
|
suppressedImeEcho = null
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
repo = newRepo
|
repo = newRepo
|
||||||
@@ -227,6 +230,7 @@ fun GitHugApp() {
|
|||||||
repo = levels[index].setup()
|
repo = levels[index].setup()
|
||||||
output = listOf("Loaded level: ${levels[index].title}")
|
output = listOf("Loaded level: ${levels[index].title}")
|
||||||
clearCommandInput()
|
clearCommandInput()
|
||||||
|
suppressedImeEcho = null
|
||||||
hintIndex = 0
|
hintIndex = 0
|
||||||
historyIndex = -1
|
historyIndex = -1
|
||||||
historyDraft = ""
|
historyDraft = ""
|
||||||
@@ -248,7 +252,17 @@ fun GitHugApp() {
|
|||||||
output = output,
|
output = output,
|
||||||
inputFieldVersion = inputFieldVersion,
|
inputFieldVersion = inputFieldVersion,
|
||||||
commandInput = commandInput,
|
commandInput = commandInput,
|
||||||
onValueChange = { commandInput = it },
|
onValueChange = {
|
||||||
|
val blockedEcho = suppressedImeEcho
|
||||||
|
if (blockedEcho != null && commandInput.text.isEmpty()) {
|
||||||
|
val blockedTrimmed = blockedEcho.trim()
|
||||||
|
if (it.text == blockedEcho || (blockedTrimmed.isNotEmpty() && it.text == blockedTrimmed)) {
|
||||||
|
return@TerminalPanel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
suppressedImeEcho = null
|
||||||
|
commandInput = it
|
||||||
|
},
|
||||||
onRun = { runCommand() },
|
onRun = { runCommand() },
|
||||||
onTab = { tabComplete() },
|
onTab = { tabComplete() },
|
||||||
onCursorLeft = { moveCursor(-1) },
|
onCursorLeft = { moveCursor(-1) },
|
||||||
@@ -376,9 +390,20 @@ private fun TerminalPanel(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
val focusManager = LocalFocusManager.current
|
||||||
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
|
|
||||||
LaunchedEffect(inputFieldVersion) {
|
LaunchedEffect(inputFieldVersion) {
|
||||||
|
if (inputFieldVersion == 0) return@LaunchedEffect
|
||||||
|
delay(75)
|
||||||
focusRequester.requestFocus()
|
focusRequester.requestFocus()
|
||||||
|
keyboardController?.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun submitCommand() {
|
||||||
|
focusManager.clearFocus(force = true)
|
||||||
|
keyboardController?.hide()
|
||||||
|
onRun()
|
||||||
}
|
}
|
||||||
|
|
||||||
Card(modifier = modifier, colors = CardDefaults.cardColors(containerColor = TerminalBackground)) {
|
Card(modifier = modifier, colors = CardDefaults.cardColors(containerColor = TerminalBackground)) {
|
||||||
@@ -427,7 +452,7 @@ private fun TerminalPanel(
|
|||||||
keyboardType = KeyboardType.Ascii,
|
keyboardType = KeyboardType.Ascii,
|
||||||
imeAction = ImeAction.Done,
|
imeAction = ImeAction.Done,
|
||||||
),
|
),
|
||||||
keyboardActions = KeyboardActions(onDone = { onRun() })
|
keyboardActions = KeyboardActions(onDone = { submitCommand() })
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -448,29 +473,35 @@ private fun SpecialKeyBar(
|
|||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
TerminalKeyButton(icon = { Icon(Icons.Filled.KeyboardTab, contentDescription = "Tab") }, onClick = onTab, modifier = Modifier.weight(1.3f))
|
TerminalKeyButton(label = "TAB", onClick = onTab, modifier = Modifier.width(68.dp))
|
||||||
TerminalKeyButton(icon = { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Move cursor left") }, onClick = onCursorLeft)
|
TerminalKeyButton(label = "←", onClick = onCursorLeft, modifier = Modifier.width(48.dp))
|
||||||
TerminalKeyButton(icon = { Icon(Icons.AutoMirrored.Filled.ArrowForward, contentDescription = "Move cursor right") }, onClick = onCursorRight)
|
TerminalKeyButton(label = "→", onClick = onCursorRight, modifier = Modifier.width(48.dp))
|
||||||
TerminalKeyButton(icon = { Icon(Icons.Filled.KeyboardArrowUp, contentDescription = "Previous command") }, onClick = onHistoryUp)
|
TerminalKeyButton(label = "↑", onClick = onHistoryUp, modifier = Modifier.width(48.dp))
|
||||||
TerminalKeyButton(icon = { Icon(Icons.Filled.KeyboardArrowDown, contentDescription = "Next command") }, onClick = onHistoryDown)
|
TerminalKeyButton(label = "↓", onClick = onHistoryDown, modifier = Modifier.width(48.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TerminalKeyButton(
|
private fun TerminalKeyButton(
|
||||||
icon: @Composable () -> Unit,
|
label: String,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Button(
|
Button(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = modifier,
|
modifier = modifier.height(34.dp),
|
||||||
|
contentPadding = PaddingValues(horizontal = 10.dp, vertical = 4.dp),
|
||||||
colors = ButtonDefaults.buttonColors(
|
colors = ButtonDefaults.buttonColors(
|
||||||
containerColor = PanelSecondary,
|
containerColor = PanelSecondary,
|
||||||
contentColor = TextPrimary
|
contentColor = TextPrimary
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
icon()
|
Text(
|
||||||
|
text = label,
|
||||||
|
fontFamily = FontFamily.Monospace,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 13.sp
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user